Codibre/fluent-iterable

View on GitHub
src/utils/get-grouping-top-hit.ts

Summary

Maintainability
A
3 hrs
Test Coverage
F
52%
import { KVGroupTransform } from '../types/base';
import { Mapper } from 'augmentative-iterable';
import { prepare } from '../types-internal/prepare';
import { AnyMapper, FunctionAnyMapper } from '../types-internal';
import { constant, identity } from './utils';
import { Choose } from '../types';

const noDistinct = constant(1);

/**
 * Returns a functions that make the group operations to get only one item, per group key,
 * The item kept is the preferred one, according to a given choosing criteria,
 * @param choose must return the preferred value over two provided
 */
export function getGroupingTopHit<K, T>(
  choose: Choose<T>,
): KVGroupTransform<K, T>;
/**
 * Returns a functions that make the group operations get distinct items, according to a giving criteria,
 * keeping the preferred one, according to a given choosing criteria,
 * @param distinct must return the distinction key. The distinction is respected by group
 * @param choose must return the preferred value over two provided
 * @param mapper The property name you want to map for the value
 */
export function getGroupingTopHit<K, T, KT extends keyof T>(
  distinct: AnyMapper<T>,
  choose: Choose<T[KT]>,
  mapper: KT,
): KVGroupTransform<K, T, T[KT][]>;
/**
 * Returns a functions that make the group operations get distinct items, according to a giving criteria,
 * keeping the preferred one, according to a given choosing criteria,
 * @param distinct must return the distinction key. The distinction is respected by group
 * @param choose must return the preferred value over two provided
 * @param mapper If informed, the mapping operation to define the grouping value. If not, the original value is assumed
 */
export function getGroupingTopHit<K, T, NewT = T>(
  distinct: AnyMapper<T>,
  choose: Choose<NewT>,
  mapper?: Mapper<T, NewT>,
): KVGroupTransform<K, T, NewT>;
export function getGroupingTopHit<K, T, NewT = T>(
  distinct: AnyMapper<T> | Choose<NewT>,
  choose?: Choose<NewT>,
  mapper: Mapper<T, NewT> | keyof T = identity as any,
): KVGroupTransform<K, T, NewT> {
  const preparedMapper: FunctionAnyMapper<T> = prepare(mapper);
  let preparedDistinct: FunctionAnyMapper<T>;
  if (!choose) {
    preparedDistinct = noDistinct;
    choose = distinct as Choose<NewT>;
    const chosenMap = new Map<K, [NewT, number]>();
    return function (k: K, v: T, previous: NewT[]) {
      const mapped = preparedMapper(v);
      const prev = chosenMap.get(k);
      if (!prev) {
        chosenMap.set(k, [mapped, previous.length]);
        return [mapped];
      }
      const chosen = choose!(prev[0], mapped);
      if (chosen !== prev[0]) {
        chosenMap.set(k, [chosen, prev[1]]);
        previous[prev[1]] = chosen;
      }
    };
  }
  preparedDistinct = prepare(distinct as AnyMapper<T>);
  const groupMap = new Map<K, Map<unknown, [NewT, number]>>();

  return function (k: K, v: T, previous: NewT[]) {
    let chosenMap = groupMap.get(k);
    if (!chosenMap) {
      chosenMap = new Map();
      groupMap.set(k, chosenMap);
    }
    const mapped = preparedMapper(v);
    const chosenKey = preparedDistinct(v);
    const prev = chosenMap.get(chosenKey);
    if (!prev) {
      chosenMap.set(chosenKey, [mapped, previous.length]);
      return [mapped];
    }
    const chosen = choose(prev[0], mapped);
    if (chosen !== prev[0]) {
      chosenMap.set(chosenKey, [chosen, prev[1]]);
      previous[prev[1]] = chosen;
    }
  };
}