All files / iterable/src distinct.ts

100% Statements 9/9
100% Branches 4/4
100% Functions 3/3
100% Lines 8/8

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 521x                                                                     2x 2x           2x 8x   8x 6x 6x        
import { identity, Mapper } from "@benneq/function";
 
/**
 * Only emit values of an {@link Iterable} that have not been emitted yet.
 *
 * The values are by default compared by identity (`===`). An optional
 * {@link Mapper} function can be used to map the values to a different
 * representation for the comparison.
 *
 * @example
 * Distinct by value
 * ```ts
 * const distinctByValue = distinct();
 * const distinctValues = distinctByValue([1,2,1,3]);
 * console.log(distinctValues); // [1,2,3]
 * ```
 *
 * @example
 * Distinct by `keyMapper` {@link Function}
 * ```ts
 * const distinctByValue = distinct(isEven);
 * const distinctValues = distinctByValue([1,2,1,3]);
 * console.log(distinctValues); // [1,2]
 * ```
 *
 * @see {@link filter} for similar operations.
 *
 * @typeParam T - the {@link Iterable} value type
 * @param keyMapper - a {@link Function} that maps an element into a key
 * @returns an {@link Iterable} containing only unique values
 */
type Distinct = <T>(
  keyMapper?: Mapper<T, unknown>
) => (iterable: Iterable<T>) => IterableIterator<T>;
 
export const distinct: Distinct = (keyMapper = identity) =>
  function* (
    iterable,
    // internal variables:
    seen = new Set<unknown>(),
    key: unknown = undefined
  ) {
    for (const value of iterable) {
      key = keyMapper(value);
 
      if (!seen.has(key)) {
        seen.add(key);
        yield value;
      }
    }
  };