@rimbu/stream
Version:
Efficient structure representing a sequence of elements, with powerful operations for TypeScript
282 lines • 14.3 kB
JavaScript
import { Eq, CollectFun, } from '@rimbu/common';
import { Reducer, Stream } from '@rimbu/stream';
import { AsyncReducer, AsyncStream, } from '@rimbu/stream/async';
export var AsyncTransformer;
(function (AsyncTransformer) {
/**
* Returns an AsyncTransformer based on a given synchronous or asynchronous transformer.
* @param transformer - the transformer to convert
* @typeparam T - the input element type
* @typeparam R - the result stream element type
*/
function from(transformer) {
return AsyncReducer.from(transformer);
}
AsyncTransformer.from = from;
/**
* Returns an async transformer that produces windows/collections of `windowSize` size, each
* window starting `skipAmount` of elements after the previous, and optionally collected
* by a custom reducer.
* @typeparam T - the input element type
* @typeparam R - the window type
* @param windowSize - the amount of elements for each window
* @param options - (optional) object specifying the following properties<br/>
* - skipAmount: (default: `windowSize`) the amount of elements between the start of each window<br/>
* - collector: (default: Reducer.toArray()) the reducer to use to convert elements to windows
* @example
* ```ts
* await AsyncStream.of(1, 2, 3, 4, 5, 6)
* .transform(AsyncTransformer.window(3))
* .toArray()
* // => [[1, 2, 3], [4, 5, 6]]
* ```
*/
AsyncTransformer.window = (windowSize, options = {}) => {
const { skipAmount = windowSize, collector = Reducer.toArray(), } = options;
return AsyncReducer.create(() => new Set(), async (state, elem, index) => {
for (const instance of state) {
if (instance.index >= windowSize || instance.halted) {
state.delete(instance);
}
else {
await instance.next(elem);
}
}
if (index % skipAmount === 0) {
const newInstance = await AsyncReducer.from(collector).compile();
await newInstance.next(elem);
state.add(newInstance);
}
return state;
}, async (state, _, halted) => {
if (halted) {
return AsyncStream.empty();
}
return AsyncStream.from(state).collect((instance, _, skip) => instance.index === windowSize ? instance.getOutput() : skip);
});
};
/**
* Returns an async transformer that applies the given flatMap function to each element of the input stream,
* and concatenates all the resulting resulting streams into one stream.
* @typeparam T - the input element type
* @typeparam T2 - the output element type
* @param flatMapFun - a potentially async function that maps each input element to an `AsyncStreamSource`.
* The function receives three parameters:<br/>
* - `value`: the current element being processed<br/>
* - `index`: the index of the current element in the input stream<br/>
* - `halt`: a function that can be called to halt further processing of the input stream<br/>
*/
function flatMap(flatMapFun) {
return AsyncReducer.createOutput(() => AsyncStream.empty(), (state, next, index, halt) => flatMapFun(next, index, halt), (state, _, halted) => (halted ? AsyncStream.empty() : state));
}
AsyncTransformer.flatMap = flatMap;
/**
* Returns an async transformer that applies the given flatMap function to each element of the input stream,
* and concatenates all the resulting resulting streams into one stream, where each resulting element is tupled
* with the originating input element.
* @typeparam T - the input element type
* @typeparam T2 - the output element type
* @param flatMapFun - a potentially async function that maps each input element to an `AsyncStreamSource`.
* The function receives three parameters:<br/>
* - `value`: the current element being processed<br/>
* - `index`: the index of the current element in the input stream<br/>
* - `halt`: a function that can be called to halt further processing of the input stream<br/>
*/
function flatZip(flatMapFun) {
return flatMap(async (value, index, halt) => AsyncStream.from(await flatMapFun(value, index, halt)).mapPure((stream) => [value, stream]));
}
AsyncTransformer.flatZip = flatZip;
/**
* Returns an async transformer that filters elements from the input stream based on the provided predicate function.
* @typeparam T - the type of elements in the input stream
* @param pred - a potentially async predicate function that determines whether an element should be included in the output stream, receiving:<br/>
* - `value`: the current element being processed<br/>
* - `index`: the index of the current element in the input stream<br/>
* - `halt`: a function that can be called to halt further processing of the input stream
* @param options - (optional) object specifying the following properties:<br/>
* - negate: (default: false) if true, the predicate will be negated
* @note if the predicate is a type guard, the return type is automatically inferred
*/
AsyncTransformer.filter = (pred, options = {}) => {
const { negate = false } = options;
return flatMap(async (value, index, halt) => (await pred(value, index, halt)) !== negate
? AsyncStream.of(value)
: AsyncStream.empty());
};
/**
* Returns an `AsyncTransformer` instance that converts or filters its input values using given `collectFun` before passing them to the reducer.
* @param collectFun - a potentially async function receiving the following arguments, and returns a new value or `skip` if the value should be skipped:<br/>
* - `value`: the next value<br/>
* - `index`: the value index<br/>
* - `skip`: a token that, when returned, will not add a value to the resulting collection<br/>
* - `halt`: a function that, when called, ensures no next elements are passed
* @typeparam T - the input element type
* @typeparam R - the result element type
*/
function collect(collectFun) {
return flatMap(async (value, index, halt) => {
const result = await collectFun(value, index, CollectFun.Skip, halt);
return CollectFun.Skip === result
? AsyncStream.empty()
: AsyncStream.of(result);
});
}
AsyncTransformer.collect = collect;
/**
* Returns an `AsyncTransformer` that inserts the given `sep` stream source elements between each received input element.
* @param sep - the async StreamSource to insert between each received element
* @typeparam T - the input and output element type
*/
function intersperse(sep) {
return flatMap((value, index) => index === 0 ? AsyncStream.of(value) : AsyncStream.from(sep).append(value));
}
AsyncTransformer.intersperse = intersperse;
/**
* Returns an `AsyncTransformer` that outputs the index of each received element that satisfies the given predicate.
* @param pred - a potentially async predicate function taking an element
* @param options - (optional) object specifying the following properties<br/>
* - negate: (default: false) when true will negate the given predicate
* @typeparam T - the input element type
*/
function indicesWhere(pred, options = {}) {
const { negate = false } = options;
return flatMap((value, index) => pred(value) !== negate ? AsyncStream.of(index) : AsyncStream.empty());
}
AsyncTransformer.indicesWhere = indicesWhere;
/**
* Returns an `AsyncTransformer` that applies the given `pred` function to each received element, and collects the received elements
* into a `collector` that will be returned as output every time the predicate returns true.
* @typeparam T - the input element type
* @typeparam R - the collector result type
* @param pred - a potentially async predicate function taking an element
* @param options - (optional) object specifying the following properties<br/>
* - negate: (default: false) when true will negate the given predicate<br/>
* - collector: (default: Reducer.toArray()) an AsyncReducer that can accept multiple values and reduce them into a single value of type `R`.
*/
function splitWhere(pred, options = {}) {
const { negate = false, collector = Reducer.toArray(), } = options;
return AsyncReducer.create(async () => ({
collection: await AsyncReducer.from(collector).compile(),
done: false,
}), async (state, nextValue, index) => {
if (state.done) {
state.done = false;
state.collection = await AsyncReducer.from(collector).compile();
}
if ((await pred(nextValue, index)) === negate) {
await state.collection.next(nextValue);
}
else {
state.done = true;
}
return state;
}, (state, _, halted) => state.done !== halted
? AsyncStream.of(state.collection.getOutput())
: AsyncStream.empty());
}
AsyncTransformer.splitWhere = splitWhere;
/**
* Returns an `AsyncTransformer` that collects the received elements
* into a `collector` that will be returned as output every time the input matches the given `sepElem` value.
* @typeparam T - the input element type
* @typeparam R - the collector result type
* @param pred - a potentially async predicate function taking an element
* @param options - (optional) object specifying the following properties<br/>
* - eq - (default: `Eq.objectIs`) the equality testing function
* - negate: (default: false) when true will negate the given predicate<br/>
* - collector: (default: Reducer.toArray()) an AsyncReducer that can accept multiple values and reduce them into a single value of type `R`.
*/
function splitOn(sepElem, options = {}) {
const { eq = Eq.objectIs, negate = false, collector = Reducer.toArray(), } = options;
return AsyncReducer.create(async () => ({
collection: await AsyncReducer.from(collector).compile(),
done: false,
}), async (state, nextValue) => {
if (state.done) {
state.done = false;
state.collection = await AsyncReducer.from(collector).compile();
}
if (eq(nextValue, sepElem) === negate) {
await state.collection.next(nextValue);
}
else {
state.done = true;
}
return state;
}, (state, _, halted) => state.done !== halted
? AsyncStream.of(state.collection.getOutput())
: AsyncStream.empty());
}
AsyncTransformer.splitOn = splitOn;
/**
* Returns an `AsyncTransformer` that collects the received elements
* into a `collector` that will be returned as output every time the input matches the given `sepSlice` sequence of elements.
* @typeparam T - the input element type
* @typeparam R - the collector result type
* @param pred - a potentially async predicate function taking an element
* @param options - (optional) object specifying the following properties<br/>
* - eq - (default: `Eq.objectIs`) the equality testing function
* - collector: (default: Reducer.toArray()) an AsyncReducer that can accept multiple values and reduce them into a single value of type `R`.
*/
function splitOnSlice(sepSlice, options = {}) {
const { eq = Eq.objectIs, collector = Reducer.toArray(), } = options;
return AsyncReducer.create(async () => ({
done: false,
instances: new Map(),
buffer: [],
result: await AsyncReducer.from(collector).compile(),
}), async (state, nextValue) => {
if (state.done) {
state.result = await AsyncReducer.from(collector).compile();
state.done = false;
}
for (const [instance, startIndex] of state.instances) {
await instance.next(nextValue);
if (instance.halted) {
state.instances.delete(instance);
}
if (await instance.getOutput()) {
state.done = true;
await AsyncStream.from(Stream.fromArray(state.buffer, {
range: { end: [startIndex, false] },
})).forEachPure(state.result.next);
state.buffer = [];
state.instances.clear();
return state;
}
}
const nextStartsWith = await AsyncReducer.startsWithSlice(sepSlice, {
eq,
}).compile();
await nextStartsWith.next(nextValue);
if (await nextStartsWith.getOutput()) {
state.done = true;
await AsyncStream.from(state.buffer).forEachPure(state.result.next);
state.buffer = [];
state.instances.clear();
return state;
}
else if (!nextStartsWith.halted) {
state.instances.set(nextStartsWith, state.buffer.length);
}
if (state.instances.size === 0) {
await state.result.next(nextValue);
}
else {
state.buffer.push(nextValue);
}
return state;
}, async (state, _, halted) => {
if (state.done === halted) {
return AsyncStream.empty();
}
if (halted) {
await AsyncStream.from(state.buffer).forEachPure(state.result.next);
state.buffer = [];
}
return AsyncStream.of(state.result.getOutput());
});
}
AsyncTransformer.splitOnSlice = splitOnSlice;
})(AsyncTransformer || (AsyncTransformer = {}));
//# sourceMappingURL=async-transformer.mjs.map