import { HashableEqualityComparer } from "../../collections/equalityComparison"; import { HashSet } from "../../collections/hashSet"; import { PipeBody, PipeFunction } from "../../pipables"; import type { LinqWrapper } from "../linqWrapper"; import { IntermediateLinqWrapper } from "../internal"; import { SequenceElementSelector } from "./typing"; export function distinct(comparer?: HashableEqualityComparer): PipeBody, LinqWrapper> { return target => { if (target instanceof DistinctLinqWrapper) { const state = target.__state; // Trivial: distinct gets chained multiple times. if (state.keySelector == null) return target; } return new DistinctLinqWrapper({ iterable: target.unwrap(), comparer, }); }; } distinct satisfies PipeFunction; export function distinctBy(keySelector: SequenceElementSelector, comparer?: HashableEqualityComparer): PipeBody, LinqWrapper> { return target => { if (target instanceof DistinctLinqWrapper) { const state = target.__state; // Trivial: distinct gets chained multiple times with the same keySelector (while almost impossible) if (state.keySelector == keySelector) return target; } return new DistinctLinqWrapper({ iterable: target.unwrap(), keySelector, comparer, }); }; } distinctBy satisfies PipeFunction; interface DistinctIteratorInfo { readonly iterable: Iterable; keySelector?: SequenceElementSelector; comparer?: HashableEqualityComparer } class DistinctLinqWrapper extends IntermediateLinqWrapper> { public override *[Symbol.iterator](): Iterator { const { iterable, keySelector, comparer } = this.__state; const seenKeys = comparer ? new HashSet(comparer) : new Set(); if (keySelector) { for (const e of iterable) { const key = keySelector(e); if (seenKeys.has(key)) continue; seenKeys.add(key); yield e; } } else { for (const e of iterable) { if (seenKeys.has(e)) continue; seenKeys.add(e); yield e; } } } }