@johngw/stream
Version:
Reactive programming tools using the WHATWG Streams API.
78 lines • 2.59 kB
JavaScript
import { pipeFlushes } from '@johngw/stream-common/Stream';
/**
* A TransformStream that queues all items from a ReadableStream that are distinct by comparison from previous items.
*
* If a `selector` function is provided, then it will project each value from the source observable into a new value that it will
* check for equality with previously projected values. If the `selector` function is not provided, it will use each value from the
* source observable directly with an equality check against previous values.
*
* A long-running `distinct` use might result in memory leaks. To help alleviate this in some scenarios, an optional `flushes` parameter
* is also provided so that the internal `Set` can be "flushed", basically clearing it of values.
*
* @group Transformers
* @example
* A simple example with numbers
*
* ```
* --1--1--2--2--2--1--2--3--4--3--2--1--
*
* distinct()
*
* --1-----2--------------3--4-----------
* ```
*
* An example using the `selector` function
*
* ```
* --{a:4,n:'f'}--{a:7,n:'b'}--{a:5,n:'f'}--
*
* distinct({ selector: ({ n }) => n })
*
* --{a:4,n:'f'}--{a:7,n:'b'}---------------
* ```
*
* An example using the `flushes` parameter.
*
* ```
* --1--1--2--2--2--1--2--3--4--3--2--1--
*
* distinct({
* flushes:
* -------------F------------------------
* })
*
* --1-----2-----2--1-----3--4-----------
* ```
*/
export function distinct({ flushes, ignoreFlushErrors, selector, } = {}) {
const set = new Set();
const finishAbortController = new AbortController();
const flushesAbortController = new AbortController();
pipeFlushes(() => set.clear(), (error) => flushesAbortController.abort(error), { flushes, ignoreFlushErrors, signal: finishAbortController.signal });
return new TransformStream({
start: flushes && !ignoreFlushErrors
? (controller) => {
flushesAbortController.signal.addEventListener('abort', () => {
set.clear();
controller.error(flushesAbortController.signal.reason);
});
}
: undefined,
transform(chunk, controller) {
const key = selector ? selector(chunk) : chunk;
if (!set.has(key)) {
set.add(key);
controller.enqueue(chunk);
}
},
flush() {
set.clear();
finishAbortController.abort();
},
cancel() {
set.clear();
finishAbortController.abort();
},
});
}
//# sourceMappingURL=distinct.js.map