0no-co / wonka

🎩 A tiny but capable push & pull stream library for TypeScript and Flow
MIT License
709 stars 29 forks source link

Add distinct operator #63

Closed kitten closed 2 years ago

kitten commented 4 years ago

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)

zenios commented 4 years ago

Isnt distinct a combination of scan and skipwhile?

kitten commented 4 years ago

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.

dlindenkreuz commented 1 year ago

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.