Closed kitten closed 2 years ago
Isnt distinct a combination of scan and skipwhile?
This can indeed be done using scan
and filter
and a subsequent map
. (All take
and skip
operators will permanently skip or take values, so for instance skipWhile
will let values through once the predicate has failed just once)
I think this is a very common operation to create, so I feel like it should be simplified.
In case it helps anyone, here is my naive implementation:
const eq = (lhs: unknown, rhs: unknown) => lhs === rhs;
const distinct = <T>(source: Source<T>, compare = eq) =>
pipe(
source,
scan((acc, v) => [acc.pop()!, v], [] as T[]),
filter(([prev, val]) => !compare(prev, val)),
map(([_, val]) => val)
);
// usage
pipe(
fromArray([1, 2, 3, 4, 5, 6, 7, 8]),
map((v) => Math.floor(v / 4)),
distinct,
forEach(console.log)
);
// without distinct: logs 0, 0, 0, 1, 1, 1, 1, 2
// with distinct: logs 0, 1, 2
Bonus points for a more concise variant that re-uses the accumulator instead of recreating it.
This should be able to filter out values when they're not distinct. Instead of just using reference equality there could be an
isDistinct
predicate function input.So the default usage may be:
distinct((a, b) => a === b)