UNPKG

@rimbu/stream

Version:

Efficient structure representing a sequence of elements, with powerful operations for TypeScript

1,167 lines 51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Reducer = void 0; var tslib_1 = require("tslib"); var base_1 = require("@rimbu/base"); var common_1 = require("@rimbu/common"); var stream_1 = require("@rimbu/stream"); function identity(value) { return value; } /** * Combines multiple reducers in an object's values of the same input type into a single reducer that * forwards each incoming value to all reducers, and when output is requested will return an object containing * the corresponding output of each reducer at the matching object property. */ function combineObj(reducerObj) { return Reducer.create(function (initHalt) { var result = {}; var allHalted = true; for (var key in reducerObj) { var instance = reducerObj[key].compile(); allHalted = allHalted && instance.halted; result[key] = instance; } if (allHalted) { initHalt(); } return result; }, function (state, elem, index, halt) { var allHalted = true; for (var key in state) { var reducerInstance = state[key]; if (!reducerInstance.halted) { reducerInstance.next(elem); allHalted = allHalted && reducerInstance.halted; } } if (allHalted) { halt(); } return state; }, function (state) { var result = {}; for (var key in state) { result[key] = state[key].getOutput(); } return result; }); } /** * Combines multiple reducers in an array of the same input type into a single reducer that * forwards each incoming value to all reducers, and when output is requested will return an array containing * the corresponding output of each reducer. */ function combineArr() { var reducers = []; for (var _i = 0; _i < arguments.length; _i++) { reducers[_i] = arguments[_i]; } return Reducer.create(function (initHalt) { var allHalted = true; var result = reducers.map(function (reducer) { var instance = reducer.compile(); allHalted = allHalted && instance.halted; return instance; }); if (allHalted) { initHalt(); } return result; }, function (state, elem, index, halt) { var allHalted = true; var i = -1; var len = state.length; while (++i < len) { var reducerInstance = state[i]; if (!reducerInstance.halted) { reducerInstance.next(elem); allHalted = allHalted && reducerInstance.halted; } } if (allHalted) { halt(); } return state; }, function (state) { return state.map(function (reducerInstance) { return reducerInstance.getOutput(); }); }); } var Reducer; (function (Reducer) { /** * A base class that can be used to easily create `Reducer` instances. * @typeparam I - the input value type * @typeparam O - the output value type * @typeparam S - the internal state type */ var Base = /** @class */ (function () { function Base(init, next, stateToResult) { this.init = init; this.next = next; this.stateToResult = stateToResult; } Base.prototype.filterInput = function (pred, options) { var _this = this; if (options === void 0) { options = {}; } var _a = options.negate, negate = _a === void 0 ? false : _a; return create(function () { return _this.compile(); }, function (state, elem, index, halt) { if (pred(elem, index, halt) !== negate) { state.next(elem); if (state.halted) { halt(); } } return state; }, function (state) { return state.getOutput(); }); }; Base.prototype.mapInput = function (mapFun) { var _this = this; return create(this.init, function (state, elem, index, halt) { return _this.next(state, mapFun(elem, index), index, halt); }, this.stateToResult); }; Base.prototype.flatMapInput = function (flatMapFun) { var _this = this; return create(function () { return _this.compile(); }, function (state, elem, index, halt) { if (state.halted) { halt(); return state; } var elems = flatMapFun(elem, index); var iter = stream_1.Stream.from(elems)[Symbol.iterator](); var done = Symbol(); var value; while (done !== (value = iter.fastNext(done))) { state.next(value); if (state.halted) { halt(); break; } } return state; }, function (state) { return state.getOutput(); }); }; Base.prototype.collectInput = function (collectFun) { var _this = this; return create(function () { return _this.compile(); }, function (state, elem, index, halt) { var nextElem = collectFun(elem, index, common_1.CollectFun.Skip, halt); if (common_1.CollectFun.Skip !== nextElem) { state.next(nextElem); if (state.halted) { halt(); } } return state; }, function (state) { return state.getOutput(); }); }; Base.prototype.mapOutput = function (mapFun) { var _this = this; return create(this.init, this.next, function (state, index, halted) { return mapFun(_this.stateToResult(state, index, halted), index, halted); }); }; Base.prototype.takeOutput = function (amount) { var _this = this; if (amount <= 0) { return create(function (initHalt) { initHalt(); return _this.init(initHalt); }, this.next, this.stateToResult); } return create(this.init, function (state, next, index, halt) { if (index >= amount - 1) { halt(); } return _this.next(state, next, index, halt); }, this.stateToResult); }; Base.prototype.takeOutputUntil = function (pred, options) { var _this = this; if (options === void 0) { options = {}; } var _a = options.negate, negate = _a === void 0 ? false : _a; return create(this.init, function (state, next, index, halt) { var nextState = _this.next(state, next, index, halt); var nextOutput = _this.stateToResult(nextState, index, false); if (pred(nextOutput, index) !== negate) { halt(); } return nextState; }, this.stateToResult); }; Base.prototype.takeInput = function (amount) { if (amount <= 0) { return create(this.init, identity, this.stateToResult); } return this.filterInput(function (_, i, halt) { if (i >= amount - 1) { halt(); } return i < amount; }); }; Base.prototype.dropInput = function (amount) { if (amount <= 0) { return this; } return this.filterInput(function (_, i) { return i >= amount; }); }; Base.prototype.sliceInput = function (from, amount) { if (from === void 0) { from = 0; } if (undefined === amount) return this.dropInput(from); if (amount <= 0) return create(this.init, identity, this.stateToResult); if (from <= 0) return this.takeInput(amount); return this.takeInput(amount).dropInput(from); }; Base.prototype.chain = function (nextReducers) { var _this = this; return Reducer.create(function (initHalt) { var iterator = stream_1.Stream.from(nextReducers)[Symbol.iterator](); var activeInstance = _this.compile(); if (undefined !== activeInstance && activeInstance.halted) { var output = activeInstance.getOutput(); do { var creator = iterator.fastNext(); if (undefined === creator) { initHalt(); return { activeInstance: activeInstance, iterator: iterator, }; } activeInstance = (0, common_1.OptLazy)(creator, output).compile(); output = activeInstance.getOutput(); } while (activeInstance.halted); } return { activeInstance: activeInstance, iterator: iterator, }; }, function (state, next, index, halt) { state.activeInstance.next(next); while (state.activeInstance.halted) { var output = state.activeInstance.getOutput(); var creator = state.iterator.fastNext(); if (undefined === creator) { halt(); return state; } state.activeInstance = (0, common_1.OptLazy)(creator, output).compile(); } return state; }, function (state) { return state.activeInstance.getOutput(); }); }; Base.prototype.compile = function () { return new Reducer.InstanceImpl(this); }; return Base; }()); Reducer.Base = Base; /** * The default `Reducer.Impl` implementation. * @typeparam I - the input element type * @typeparam O - the output element type * @typeparam S - the reducer state type */ var InstanceImpl = /** @class */ (function () { function InstanceImpl(reducer) { var _this = this; this.reducer = reducer; this.___index = 0; this.___halted = false; this.halt = function () { _this.___halted = true; }; this.next = function (value) { if (_this.___halted) { throw new Reducer.ReducerHaltedError(); } _this.___state = _this.reducer.next(_this.___state, value, _this.___index++, _this.halt); }; this.___state = reducer.init(this.halt); } Object.defineProperty(InstanceImpl.prototype, "halted", { get: function () { return this.___halted; }, enumerable: false, configurable: true }); Object.defineProperty(InstanceImpl.prototype, "index", { get: function () { return this.___index; }, enumerable: false, configurable: true }); InstanceImpl.prototype.getOutput = function () { return this.reducer.stateToResult(this.___state, this.index, this.halted); }; return InstanceImpl; }()); Reducer.InstanceImpl = InstanceImpl; /** * Returns a `Reducer` with the given options: * @param init - the initial state value * @param next - returns the next state value based on the given inputs:<br/> * - current: the current state<br/> * - next: the current input value<br/> * - index: the input index value<br/> * - halt: function that, when called, ensures no more elements are passed to the reducer * @param stateToResult - a function that converts the current state to an output value * @typeparam I - the input value type * @typeparam O - the output value type * @typeparam S - the internal state type * @example * ```ts * const evenNumberOfOnes = Reducer.create( * true, * (current, value: number) => (value === 1 ? !current : current), * (state) => (state ? 'even' : 'not even') * ); * const result = Stream.of(1, 2, 3, 2, 1).reduce(evenNumberOfOnes); * console.log(result); * // => 'even' * ``` */ function create(init, next, stateToResult) { return new Reducer.Base(init, next, stateToResult); } Reducer.create = create; /** * Returns a `Reducer` of which the input, state, and output types are the same. * @param init - the initial state value * @param next - returns the next state value based on the given inputs:<br/> * - current: the current state<br/> * - next: the current input value<br/> * - index: the input index value<br/> * - halt: function that, when called, ensures no more elements are passed to the reducer * @param stateToResult - (optional) a function that converts the current state to an output value * @typeparam T - the overall value type * @example * ```ts * const sum = Reducer.createMono( * 0, * (current, value) => current + value * ); * const result = Stream.of(1, 2, 3, 2, 1).reduce(sum); * console.log(result); * // => 9 * ``` */ function createMono(init, next, stateToResult) { return create(init, next, stateToResult !== null && stateToResult !== void 0 ? stateToResult : identity); } Reducer.createMono = createMono; /** * Returns a `Reducer` of which the state and output types are the same. * @param init - the initial state value * @param next - returns the next state value based on the given inputs:<br/> * - current: the current state<br/> * - next: the current input value<br/> * - index: the input index value<br/> * - halt: function that, when called, ensures no more elements are passed to the reducer * @param stateToResult - (optional) a function that converts the current state to an output value * @typeparam I - the input value type * @typeparam O - the output value type * @example * ```ts * const boolToString = Reducer.createOutput( * '', * (current, value: boolean) => current + (value ? 'T' : 'F') * ); * const result = Stream.of(true, false, true).reduce(boolToString); * console.log(result); * // => 'TFT' * ``` */ function createOutput(init, next, stateToResult) { return create(init, next, stateToResult !== null && stateToResult !== void 0 ? stateToResult : identity); } Reducer.createOutput = createOutput; /** * Returns a `Reducer` that uses the given `init` and `next` values to fold the input values into * result values. * @param init - an (optionally lazy) initial result value * @param next - a function taking the following arguments:<br/> * - current - the current result value<br/> * - value - the next input value<br/> * - index: the input index value<br/> * - halt: function that, when called, ensures no more elements are passed to the reducer * @typeparam T - the input type * @typeparam R - the output type */ function fold(init, next) { return Reducer.createOutput(function () { return (0, common_1.OptLazy)(init); }, next); } Reducer.fold = fold; /** * A `Reducer` that sums all given numeric input values. * @example * ```ts * console.log(Stream.range({ amount: 5 }).reduce(Reducer.sum)) * // => 10 * ``` */ Reducer.sum = createMono(function () { return 0; }, function (state, next) { return state + next; }); /** * A `Reducer` that calculates the product of all given numeric input values. * @example * ```ts * console.log(Stream.range({ start: 1, amount: 5 }).reduce(product)) * // => 120 * ``` */ Reducer.product = createMono(function () { return 1; }, function (state, next, _, halt) { if (0 === next) halt(); return state * next; }); /** * A `Reducer` that calculates the average of all given numberic input values. * @example * ```ts * console.log(Stream.range({ amount: 5 }).reduce(Reducer.average)); * // => 2 * ``` */ Reducer.average = createMono(function () { return 0; }, function (avg, value, index) { return avg + (value - avg) / (index + 1); }); /** * Returns a `Reducer` that remembers the minimum value of the inputs using the given `compFun` to compare input values * @param compFun - a comparison function for two input values, returning 0 when equal, positive when greater, negetive when smaller * @param otherwise - (default: undefineds) a fallback value when there were no input values given * @typeparam T - the element type * @typeparam O - the fallback value type * @example * ```ts * const stream = Stream.of('abc', 'a', 'abcde', 'ab') * console.log(stream.minBy((s1, s2) => s1.length - s2.length)) * // 'a' * ``` */ Reducer.minBy = function (compFun, otherwise) { var token = Symbol(); return create(function () { return token; }, function (state, next) { if (token === state) { return next; } return compFun(state, next) < 0 ? state : next; }, function (state) { return (token === state ? (0, common_1.OptLazy)(otherwise) : state); }); }; /** * Returns a `Reducer` that remembers the minimum value of the numberic inputs. * @param otherwise - (default: undefined) a fallback value when there were no input values given * @typeparam O - the fallback value type * @example * ```ts * console.log(Stream.of(5, 3, 7, 4).reduce(Reducer.min())) * // => 3 * ``` */ // prettier-ignore Reducer.min = function (otherwise) { return create(function () { return undefined; }, function (state, next) { return undefined !== state && state < next ? state : next; }, function (state) { return state !== null && state !== void 0 ? state : (0, common_1.OptLazy)(otherwise); }); }; /** * Returns a `Reducer` that remembers the maximum value of the inputs using the given `compFun` to compare input values * @param compFun - a comparison function for two input values, returning 0 when equal, positive when greater, negetive when smaller * @param otherwise - (default: undefined) a fallback value when there were no input values given * @typeparam T - the element type * @typeparam O - the fallback value type * @example * ```ts * const stream = Stream.of('abc', 'a', 'abcde', 'ab') * console.log(stream.maxBy((s1, s2) => s1.length - s2.length)) * // 'abcde' * ``` */ Reducer.maxBy = function (compFun, otherwise) { var token = Symbol(); return create(function () { return token; }, function (state, next) { if (token === state) { return next; } return compFun(state, next) > 0 ? state : next; }, function (state) { return (token === state ? (0, common_1.OptLazy)(otherwise) : state); }); }; /** * Returns a `Reducer` that remembers the maximum value of the numberic inputs. * @param otherwise - (default: undefined) a fallback value when there were no input values given * @typeparam O - the fallback value type * @example * ```ts * console.log(Stream.of(5, 3, 7, 4).reduce(Reducer.max())) * // => 7 * ``` */ // prettier-ignore Reducer.max = function (otherwise) { return create(function () { return undefined; }, function (state, next) { return undefined !== state && state > next ? state : next; }, function (state) { return state !== null && state !== void 0 ? state : (0, common_1.OptLazy)(otherwise); }); }; /** * Returns a `Reducer` that joins the given input values into a string using the given options. * @param options - an object containing:<br/> * - sep: (optional) a seperator string value between values in the output<br/> * - start: (optional) a start string to prepend to the output<br/> * - end: (optional) an end string to append to the output<br/> * @typeparam T - the input element type * @example * ```ts * console.log(Stream.of(1, 2, 3).reduce(Reducer.join({ sep: '-' }))) * // => '1-2-3' * ``` */ function join(_a) { var _b = _a === void 0 ? {} : _a, _c = _b.sep, sep = _c === void 0 ? '' : _c, _d = _b.start, start = _d === void 0 ? '' : _d, _e = _b.end, end = _e === void 0 ? '' : _e, _f = _b.valueToString, valueToString = _f === void 0 ? String : _f; return create(function () { return ''; }, function (state, next, index) { var valueString = valueToString(next); if (index <= 0) { return start.concat(valueString); } return state.concat(sep, valueToString(next)); }, function (state) { return state.concat(end); }); } Reducer.join = join; /** * A `Reducer` that remembers the amount of input items provided. * @example * ```ts * const stream = Stream.range({ amount: 10 }) * console.log(stream.reduce(Reducer.count)) * // => 10 * ``` */ Reducer.count = create(function () { // }, identity, function (_, index) { return index; }); /** * Returns a `Reducer` that remembers the first input value. * @param otherwise - (default: undefined) a fallback value to output if no input value has been provided * @typeparam T - the input value type * @typeparam O - the fallback value type * @example * ```ts * console.log(Stream.range({ amount: 10 }).reduce(Reducer.first())) * // => 0 * ``` */ Reducer.first = function (otherwise) { return create(function () { return undefined; }, function (state, next, _, halt) { halt(); return next; }, function (state, index) { return (index <= 0 ? (0, common_1.OptLazy)(otherwise) : state); }); }; /** * Returns a `Reducer` that remembers the last input value. * @param otherwise - (default: undefined) a fallback value to output if no input value has been provided * @typeparam T - the input value type * @typeparam O - the fallback value type * @example * ```ts * console.log(Stream.range({ amount: 10 }).reduce(Reducer.last())) * // => 9 * ``` */ Reducer.last = function (otherwise) { return create(function () { return undefined; }, function (_, next) { return next; }, function (state, index) { return (index <= 0 ? (0, common_1.OptLazy)(otherwise) : state); }); }; /** * Returns a Reducer that only produces an output value when having receives exactly one * input value, otherwise will return the `otherwise` value or undefined. * @param otherwise - the fallback value to return when more or less than one value is received. * @typeparam T - the element type * @typeparam O - the fallback value type */ Reducer.single = function (otherwise) { return create(function () { return undefined; }, function (state, next, index, halt) { if (index > 1) { halt(); } return next; }, function (state, index) { return (index !== 1 ? (0, common_1.OptLazy)(otherwise) : state); }); }; /** * Returns a `Reducer` that ouputs false as long as no input value satisfies given `pred`, true otherwise. * @typeparam T - the element type * @param pred - a function taking an input value and its index, and returning true if the value satisfies the predicate * @param options - (optional) an object containing the following properties:<br/> * - negate: (default: false) when true will invert the given predicate * @example * ```ts * console.log( * Stream.range({ amount: 10 }).reduce(Reducer.some((v) => v > 5)) * ) * // => true * ``` */ function some(pred, options) { if (options === void 0) { options = {}; } return Reducer.nonEmpty.filterInput(pred, options); } Reducer.some = some; /** * Returns a `Reducer` that ouputs true as long as all input values satisfy the given `pred`, false otherwise. * @typeparam T - the element type * @param pred - a function taking an input value and its index, and returning true if the value satisfies the predicate * @param options - (optional) an object containing the following properties:<br/> * - negate: (default: false) when true will invert the given predicate * @example * ```ts * console.log( * Stream.range({ amount: 10 }).reduce(Reducer.every((v) => v < 5)) * ) * // => false * ``` */ function every(pred, options) { if (options === void 0) { options = {}; } var _a = options.negate, negate = _a === void 0 ? false : _a; return Reducer.isEmpty.filterInput(pred, { negate: !negate }); } Reducer.every = every; /** * Returns a `Reducer` that ouputs true when the received elements match the given `other` stream source according to the `eq` instance, false otherwise. * @typeparam T - the element type * @param other - a stream source containg elements to match against * @param options - (optional) an object containing the following properties:<br/> * - eq: (default: Eq.objectIs) the `Eq` instance to use to compare elements * - negate: (default: false) when true will invert the given predicate */ function equals(other, options) { if (options === void 0) { options = {}; } var _a = options.eq, eq = _a === void 0 ? common_1.Eq.objectIs : _a, _b = options.negate, negate = _b === void 0 ? false : _b; var sliceStream = stream_1.Stream.from(other); var done = Symbol(); return Reducer.create(function () { var iter = sliceStream[Symbol.iterator](); var nextSeq = iter.fastNext(done); return { iter: iter, nextSeq: nextSeq, result: false }; }, function (state, next, _, halt) { if (done === state.nextSeq) { halt(); state.result = false; return state; } if (eq(next, state.nextSeq) === negate) { halt(); state.result = false; return state; } state.nextSeq = state.iter.fastNext(done); if (done === state.nextSeq) { state.result = true; } return state; }, function (state, index, halted) { return !halted && done === state.nextSeq; }); } Reducer.equals = equals; /** * Returns a `Reducer` that outputs false as long as the given `elem` has not been encountered the given `amount` of times in the input values, true otherwise. * @typeparam T - the element type * @param elem - the element to search for * @param options - (optional) an object containing the following properties:<br/> * - amount: (detaulf: 1) the amount of elements to find * - eq: (default: Eq.objectIs) the `Eq` instance to use to compare elements * - negate: (default: false) when true will invert the given predicate * @example * ```ts * console.log(Stream.range({ amount: 10 }).reduce(Reducer.contains(5))) * // => true * ``` */ function contains(elem, options) { if (options === void 0) { options = {}; } var _a = options.amount, amount = _a === void 0 ? 1 : _a, _b = options.eq, eq = _b === void 0 ? Object.is : _b, _c = options.negate, negate = _c === void 0 ? false : _c; return Reducer.create(function (initHalt) { if (amount <= 0) { initHalt(); } return amount; }, function (state, next, _, halt) { var satisfies = eq(next, elem) !== negate; if (!satisfies) { return state; } var newRemain = state - 1; if (newRemain <= 0) { halt(); } return newRemain; }, function (state) { return state <= 0; }); } Reducer.contains = contains; /** * Returns a `Reducer` that returns true if the first input values match the given `slice` values repeated `amount` times. Otherwise, * returns false. * @param slice - a sequence of elements to match against * @param options - (optional) an object containing the following properties:<br/> * - amount: (detaulf: 1) the amount of elements to find * - eq: (default: Eq.objectIs) the `Eq` instance to use to compare elements */ function startsWithSlice(slice, options) { if (options === void 0) { options = {}; } var sliceStream = stream_1.Stream.from(slice); var done = Symbol(); var _a = options.eq, eq = _a === void 0 ? common_1.Eq.objectIs : _a, _b = options.amount, amount = _b === void 0 ? 1 : _b; return Reducer.create(function (initHalt) { var sliceIter = sliceStream[Symbol.iterator](); var sliceValue = sliceIter.fastNext(done); if (done === sliceValue || amount <= 0) { initHalt(); return { sliceIter: sliceIter, sliceValue: sliceValue, remain: 0 }; } return { sliceIter: sliceIter, sliceValue: sliceValue, remain: amount, }; }, function (state, next, _, halt) { if (done === state.sliceValue) { base_1.RimbuError.throwInvalidStateError(); } if (eq(next, state.sliceValue)) { state.sliceValue = state.sliceIter.fastNext(done); if (done === state.sliceValue) { state.remain--; if (state.remain <= 0) { halt(); } else { state.sliceIter = sliceStream[Symbol.iterator](); state.sliceValue = state.sliceIter.fastNext(done); } } } else { halt(); } return state; }, function (state) { return state.remain <= 0; }); } Reducer.startsWithSlice = startsWithSlice; /** * Returns a `Reducer` that returns true if the last input values match the given `slice` values repeated `amount` times. Otherwise, * returns false. * @param slice - a sequence of elements to match against * @param options - (optional) an object containing the following properties:<br/> * - amount: (detaulf: 1) the amount of elements to find * - eq: (default: Eq.objectIs) the `Eq` instance to use to compare elements */ function endsWithSlice(slice, options) { if (options === void 0) { options = {}; } var sliceStream = stream_1.Stream.from(slice); var done = Symbol(); var newReducerSpec = Reducer.startsWithSlice(slice, options); return Reducer.create(function (initHalt) { var sliceIter = sliceStream[Symbol.iterator](); var sliceValue = sliceIter.fastNext(done); if (done === sliceValue) { initHalt(); } return new Set([newReducerSpec.compile()]); }, function (state, nextValue) { var e_1, _a; try { for (var state_1 = tslib_1.__values(state), state_1_1 = state_1.next(); !state_1_1.done; state_1_1 = state_1.next()) { var instance = state_1_1.value; if (instance.halted) { state.delete(instance); } else { instance.next(nextValue); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (state_1_1 && !state_1_1.done && (_a = state_1.return)) _a.call(state_1); } finally { if (e_1) throw e_1.error; } } var newReducerInstance = newReducerSpec.compile(); newReducerInstance.next(nextValue); state.add(newReducerInstance); return state; }, function (state) { return state.size === 0 || stream_1.Stream.from(state).some(function (instance) { return instance.getOutput(); }); }); } Reducer.endsWithSlice = endsWithSlice; /** * Returns a `Reducer` that returns true if the input values contain the given `slice` sequence `amount` times. Otherwise, * returns false. * @param slice - a sequence of elements to match against * @param options - (optional) an object containing the following properties:<br/> * - amount: (detaulf: 1) the amount of elements to find * - eq: (default: Eq.objectIs) the `Eq` instance to use to compare elements */ function containsSlice(slice, options) { if (options === void 0) { options = {}; } var eq = options.eq, _a = options.amount, amount = _a === void 0 ? 1 : _a; return Reducer.pipe(endsWithSlice(slice, { eq: eq }), Reducer.contains(true, { amount: amount })); } Reducer.containsSlice = containsSlice; /** * A `Reducer` that takes boolean values and outputs true if all input values are true, and false otherwise. * @example * ```ts * console.log(Stream.of(true, false, true)).reduce(Reducer.and)) * // => false * ``` */ Reducer.and = createMono(function () { return true; }, function (state, next, _, halt) { if (!next) { halt(); } return next; }); /** * A `Reducer` that takes boolean values and outputs true if one or more input values are true, and false otherwise. * @example * ```ts * console.log(Stream.of(true, false, true)).reduce(Reducer.or)) * // => true * ``` */ Reducer.or = createMono(function () { return false; }, function (state, next, _, halt) { if (next) { halt(); } return next; }); /** * A `Reducer` that outputs true if no input values are received, false otherwise. * @example * ```ts * console.log(Stream.of(1, 2, 3).reduce(Reducer.isEmpty)) * // => false * ``` */ Reducer.isEmpty = createOutput(function () { return true; }, function (_, __, ___, halt) { halt(); return false; }); /** * A `Reducer` that outputs true if one or more input values are received, false otherwise. * @example * ```ts * console.log(Stream.of(1, 2, 3).reduce(Reducer.nonEmpty)) * // => true * ``` */ Reducer.nonEmpty = createOutput(function () { return false; }, function (_, __, ___, halt) { halt(); return true; }); /** * Returns a `Reducer` that always outputs the given `value`, and does not accept input values. */ function constant(value) { return Reducer.create(function (initHalt) { initHalt(); }, identity, function () { return (0, common_1.OptLazy)(value); }); } Reducer.constant = constant; /** * Returns a `Reducer` that splits the incoming values into two separate outputs based on the given `pred` predicate. Values for which the predicate is true * are fed into the `collectorTrue` reducer, and other values are fed into the `collectorFalse` instance. If no collectors are provided the values are collected * into arrays. * @param pred - a predicate receiving the value and its index * @param options - (optional) an object containing the following properties:<br/> * - collectorTrue: (default: Reducer.toArray()) a reducer that collects the values for which the predicate is true<br/> * - collectorFalse: (default: Reducer.toArray()) a reducer that collects the values for which the predicate is false * @typeparam T - the input element type * @typeparam RT - the reducer result type for the `collectorTrue` value * @typeparam RF - the reducer result type for the `collectorFalse` value * @note if the predicate is a type guard, the return type is automatically inferred * @example * ```ts * Stream.of(1, 2, 3).partition((v) => v % 2 === 0) * // => [[2], [1, 3]] * * Stream.of<number | string>(1, 'a', 'b', 2) * .partition((v): v is string => typeof v === 'string') * // => [['a', 'b'], [1, 2]] * // return type is: [string[], number[]] * * Stream.of(1, 2, 3, 4).partition( * (v) => v % 2 === 0, * { collectorTrue: Reducer.toJSSet(), collectorFalse: Reducer.sum } * ) * // => [Set(2, 4), 4] * ``` */ Reducer.partition = function (pred, options) { var _a, _b; if (options === void 0) { options = {}; } var collectorTrue = (_a = options.collectorTrue) !== null && _a !== void 0 ? _a : Reducer.toArray(); var collectorFalse = (_b = options.collectorFalse) !== null && _b !== void 0 ? _b : Reducer.toArray(); return Reducer.create(function () { return [collectorTrue.compile(), collectorFalse.compile()]; }, function (state, value, index) { var instanceIndex = pred(value, index) ? 0 : 1; state[instanceIndex].next(value); return state; }, function (state) { return state.map(function (v) { return v.getOutput(); }); }); }; /** * Returns a `Reducer` that uses the `valueToKey` function to calculate a key for each value, and feeds the tuple of the key and the value to the * `collector` reducer. Finally, it returns the output of the `collector`. If no collector is given, the default collector will return a JS multimap * of the type `Map<K, V[]>`. * @param valueToKey - function taking a value and its index, and returning the corresponding key * @param options - (optional) an object containing the following properties:<br/> * - collector: (default: Reducer.toArray()) a reducer that collects the incoming tuple of key and value, and provides the output * @typeparam T - the input value type * @typeparam K - the key type * @typeparam R - the collector output type * @example * ```ts * Stream.of(1, 2, 3).groupBy((v) => v % 2) * // => Map {0 => [2], 1 => [1, 3]} * ``` */ Reducer.groupBy = function (valueToKey, options) { if (options === void 0) { options = {}; } var _a = options.collector, collector = _a === void 0 ? Reducer.toJSMultiMap() : _a; return Reducer.create(function () { return collector.compile(); }, function (state, value, index) { var key = valueToKey(value, index); state.next([key, value]); return state; }, function (state) { return state.getOutput(); }); }; /** * Returns a `Reducer` that feeds incoming values to all reducers in the provided `reducers` source, and halts when the first * reducer in the array is halted and returns the output of that reducer. Returns the `otherwise` value if no reducer is yet halted. * @param reducers - a stream source of reducers that will receive the incoming values * @param otherwise - a fallback value to return if none of the reducers has been halted * @typeparam T - the input value type * @typeparam R - the output value type * @typeparam O - the fallback value type */ Reducer.race = function (reducers, otherwise) { return Reducer.create(function (initHalt) { var instances = stream_1.Stream.from(reducers) .map(function (reducer) { return reducer.compile(); }) .toArray(); var doneInstance = instances.find(function (instance) { return instance.halted; }); if (undefined !== doneInstance) { initHalt(); } return { instances: instances, doneInstance: doneInstance }; }, function (state, next, _, halt) { var e_2, _a; try { for (var _b = tslib_1.__values(state.instances), _c = _b.next(); !_c.done; _c = _b.next()) { var instance = _c.value; instance.next(next); if (instance.halted) { state.doneInstance = instance; halt(); return state; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_2) throw e_2.error; } } return state; }, function (state) { return state.doneInstance === undefined ? (0, common_1.OptLazy)(otherwise) : state.doneInstance.getOutput(); }); }; /** * Returns a `Reducer` that collects received input values in an array, and returns a copy of that array as an output value when requested. * @typeparam T - the element type * @param options - (optional) object specifying the following properties<br/> * - reversed: (optional) when true will create a reversed array * @example * ```ts * console.log(Stream.of(1, 2, 3).reduce(Reducer.toArray())) * // => [1, 2, 3] * console.log(Stream.of(1, 2, 3).reduce(Reducer.toArray({ reversed: true }))) * // => [3, 2, 1] * ``` */ function toArray(options) { if (options === void 0) { options = {}; } var _a = options.reversed, reversed = _a === void 0 ? false : _a; return create(function () { return []; }, function (state, next) { if (reversed) state.unshift(next); else state.push(next); return state; }, function (state) { return state.slice(); }); } Reducer.toArray = toArray; /** * Returns a `Reducer` that collects received input tuples into a mutable JS Map, and returns * a copy of that map when output is requested. * @typeparam K - the map key type * @typeparam V - the map value type * @example * ```ts * console.log(Stream.of([1, 'a'], [2, 'b']).reduce(Reducer.toJSMap())) * // Map { 1 => 'a', 2 => 'b' } * ``` */ function toJSMap() { return create(function () { return new Map(); }, function (state, next) { state.set(next[0], next[1]); return state; }, function (state) { return new Map(state); }); } Reducer.toJSMap = toJSMap; /** * Returns a `Reducer` that collects received input tuples into a mutable JS multimap, and returns * a copy of that map when output is requested. * @typeparam K - the map key type * @typeparam V - the map value type * @example * ```ts * console.log(Stream.of([1, 'a'], [2, 'b']).reduce(Reducer.toJSMap())) * // Map { 1 => 'a', 2 => 'b' } * ``` */ function toJSMultiMap() { return create(function () { return new Map(); }, function (state, _a) { var _b = tslib_1.__read(_a, 2), key = _b[0], value = _b[1]; var entry = state.get(key); if (undefined === entry) { state.set(key, [value]); } else { entry.push(value); } return state; }, function (state) { return new Map(state); }); } Reducer.toJSMultiMap = toJSMultiMap; /** * Returns a `Reducer` that collects received input values into a mutable JS Set, and returns * a copy of that map when output is requested. * @typeparam T - the element type * @example * ```ts * console.log(Stream.of(1, 2, 3).reduce(Reducer.toJSSet())) * // Set {1, 2, 3} * ``` */ function toJSSet() { return create(function () { return new Set(); }, function (state, next) { state.add(next); return state; }, function (s) { return new Set(s); }); } Reducer.toJSSet = toJSSet; /** * Returns a `Reducer` that collects 2-tuples containing keys and values into a plain JS object, and * returns a copy of that object when output is requested. * @typeparam K - the result object key type * @typeparam V - the result object value type * @example * ```ts * console.log(Stream.of(['a', 1], ['b', true]).reduce(Reducer.toJSObject())) * // { a: 1, b: true } * ``` */ function toJSObject() { return create(function () { return ({}); }, function (state, entry) { state[entry[0]] = entry[1]; return state; }, function (s) { return (tslib_1.__assign({}, s)); }); } Reducer.toJSObject = toJSObject; var InvalidCombineShapeError = /** @class */ (function (_super) { tslib_1.__extends(InvalidCombineShapeError, _super); function InvalidCombineShapeError() { return _super.call(this, 'Invalid reducer combine shape supplied') || this; } return InvalidCombineShapeError; }(common_1.ErrBase.CustomError)); Reducer.InvalidCombineShapeError = InvalidCombineShapeError; var ReducerHaltedError = /** @class */ (function (_super) { tslib_1.__extends(ReducerHaltedError, _super); function ReducerHaltedError() { return _super.call(this, 'A halted reducer cannot receive more values') || this; } return ReducerHaltedError; }(common_1.ErrBase.CustomError)); Reducer.ReducerHaltedError = ReducerHaltedError; /** * Returns a `Reducer` that combines multiple input `reducers` according to the given "shape" by providing input values to all of them and collecting the outputs in the shape. * @typeparam T - the input value type for all the reducers * @typeparam S - the desired result shape type * @param shape - a shape defining where reducer outputs will be located in the result. It can consist of a single reducer, an array of shapes, or an object with string keys and shapes as values. * @example * ```ts * const red = Reducer.combine([Reducer.sum, { av: [Reducer.average] }]) * console.log(Stream.range({amount: 9 }).reduce(red)) * // => [36, { av: [4] }] * ``` */ function combine(shape) { if (shape instanceof Reducer.Base) { return shape; } if (Array.isArray(shape)) { return combineArr.apply(void 0, tslib_1.__spreadArray([], tslib_1.__read(shape.map(function (item) { return Reducer.combine(item); })), false)); } if (typeof shape === 'object' && shape !== null) { var result = {}; for (var key in shape) { result[key] = Reducer.combine(shape[key]); } return combineObj(result); } throw new Reducer.InvalidCombineShapeError(); } Reducer.combine = combine; /** * Returns a `Reducer` instance that first applies this reducer, and then applies the given `next` reducer to each output produced * by the previous reducer. * @typeparam I - the input type of the `reducer1` reducer * @typeparam O1 - the output type of the `reducer1` reducer * @typeparam O2 - the output type of the `reducer2` reducer * @typeparam O3 - the output type of the `reducer3` reducer * @typeparam O4 - the output type of the `reducer4` reducer * @typeparam O5 - the output type of the `reducer5` reducer * @param reducer1 - the next reducer to apply to each output of this reducer. * @param reducer2 - (optional) the next reducer to apply to each output of this reducer. * @param reducer3 - (optional) the next reducer to apply to each output of this reducer. * @param reducer4 - (optional) the next reducer to apply to each output of this reducer. * @param reducer5 - (optional) the next reducer to apply to each output of this reducer. * @example * ```ts * Stream.of(1, 2, 3) * .reduce( * Reducer.pipe(Reducer.product, Reducer.sum) * ) * // => 9 * ``` */ Reducer.pipe = function () { var nextReducers = []; for (var _i = 0; _i < arguments.length; _i++) { nextReducers[_i] = arguments[_i]; } if (nextReducers.length < 2) { base_1.RimbuError.throwInvalidUsageError('Reducer.pipe should have at least two arguments'); } var _a = tslib_1.__read(nextReducers), current = _a[0], next = _a[1], others = _a.slice(2); if (others.length > 0) { return Reducer.pipe(current, Reducer.pipe.apply(Reducer, tslib_1.__spreadArray([next], tslib_1.__read(others), false))); } return Reducer.create(function (inithalt) { var currentInstance = current.co