/****************************************************************************** * Copyright 2021 TypeFox GmbH * This program and the accompanying materials are made available under the * terms of the MIT License, which is available in the project root. ******************************************************************************/ /** * The default implementation of `Stream` works with two input functions: * - The first function creates the initial state of an iteration. * - The second function gets the current state as argument and returns an `IteratorResult`. */ export class StreamImpl { constructor(startFn, nextFn) { this.startFn = startFn; this.nextFn = nextFn; } iterator() { const iterator = { state: this.startFn(), next: () => this.nextFn(iterator.state), [Symbol.iterator]: () => iterator }; return iterator; } [Symbol.iterator]() { return this.iterator(); } isEmpty() { const iterator = this.iterator(); return Boolean(iterator.next().done); } count() { const iterator = this.iterator(); let count = 0; let next = iterator.next(); while (!next.done) { count++; next = iterator.next(); } return count; } toArray() { const result = []; const iterator = this.iterator(); let next; do { next = iterator.next(); if (next.value !== undefined) { result.push(next.value); } } while (!next.done); return result; } toSet() { return new Set(this); } toMap(keyFn, valueFn) { const entryStream = this.map(element => [ keyFn ? keyFn(element) : element, valueFn ? valueFn(element) : element ]); return new Map(entryStream); } toString() { return this.join(); } concat(other) { return new StreamImpl(() => ({ first: this.startFn(), firstDone: false, iterator: other[Symbol.iterator]() }), state => { let result; if (!state.firstDone) { do { result = this.nextFn(state.first); if (!result.done) { return result; } } while (!result.done); state.firstDone = true; } do { result = state.iterator.next(); if (!result.done) { return result; } } while (!result.done); return DONE_RESULT; }); } join(separator = ',') { const iterator = this.iterator(); let value = ''; let result; let addSeparator = false; do { result = iterator.next(); if (!result.done) { if (addSeparator) { value += separator; } value += toString(result.value); } addSeparator = true; } while (!result.done); return value; } indexOf(searchElement, fromIndex = 0) { const iterator = this.iterator(); let index = 0; let next = iterator.next(); while (!next.done) { if (index >= fromIndex && next.value === searchElement) { return index; } next = iterator.next(); index++; } return -1; } every(predicate) { const iterator = this.iterator(); let next = iterator.next(); while (!next.done) { if (!predicate(next.value)) { return false; } next = iterator.next(); } return true; } some(predicate) { const iterator = this.iterator(); let next = iterator.next(); while (!next.done) { if (predicate(next.value)) { return true; } next = iterator.next(); } return false; } forEach(callbackfn) { const iterator = this.iterator(); let index = 0; let next = iterator.next(); while (!next.done) { callbackfn(next.value, index); next = iterator.next(); index++; } } map(callbackfn) { return new StreamImpl(this.startFn, (state) => { const { done, value } = this.nextFn(state); if (done) { return DONE_RESULT; } else { return { done: false, value: callbackfn(value) }; } }); } filter(predicate) { return new StreamImpl(this.startFn, state => { let result; do { result = this.nextFn(state); if (!result.done && predicate(result.value)) { return result; } } while (!result.done); return DONE_RESULT; }); } nonNullable() { return this.filter(e => e !== undefined && e !== null); } reduce(callbackfn, initialValue) { const iterator = this.iterator(); let previousValue = initialValue; let next = iterator.next(); while (!next.done) { if (previousValue === undefined) { previousValue = next.value; } else { previousValue = callbackfn(previousValue, next.value); } next = iterator.next(); } return previousValue; } reduceRight(callbackfn, initialValue) { return this.recursiveReduce(this.iterator(), callbackfn, initialValue); } recursiveReduce(iterator, callbackfn, initialValue) { const next = iterator.next(); if (next.done) { return initialValue; } const previousValue = this.recursiveReduce(iterator, callbackfn, initialValue); if (previousValue === undefined) { return next.value; } return callbackfn(previousValue, next.value); } find(predicate) { const iterator = this.iterator(); let next = iterator.next(); while (!next.done) { if (predicate(next.value)) { return next.value; } next = iterator.next(); } return undefined; } findIndex(predicate) { const iterator = this.iterator(); let index = 0; let next = iterator.next(); while (!next.done) { if (predicate(next.value)) { return index; } next = iterator.next(); index++; } return -1; } includes(searchElement) { const iterator = this.iterator(); let next = iterator.next(); while (!next.done) { if (next.value === searchElement) { return true; } next = iterator.next(); } return false; } flatMap(callbackfn) { return new StreamImpl(() => ({ this: this.startFn() }), (state) => { do { if (state.iterator) { const next = state.iterator.next(); if (next.done) { state.iterator = undefined; } else { return next; } } const { done, value } = this.nextFn(state.this); if (!done) { const mapped = callbackfn(value); if (isIterable(mapped)) { state.iterator = mapped[Symbol.iterator](); } else { return { done: false, value: mapped }; } } } while (state.iterator); return DONE_RESULT; }); } flat(depth) { if (depth === undefined) { depth = 1; } if (depth <= 0) { return this; } const stream = depth > 1 ? this.flat(depth - 1) : this; return new StreamImpl(() => ({ this: stream.startFn() }), (state) => { do { if (state.iterator) { const next = state.iterator.next(); if (next.done) { state.iterator = undefined; } else { return next; } } const { done, value } = stream.nextFn(state.this); if (!done) { if (isIterable(value)) { state.iterator = value[Symbol.iterator](); } else { return { done: false, value: value }; } } } while (state.iterator); return DONE_RESULT; }); } head() { const iterator = this.iterator(); const result = iterator.next(); if (result.done) { return undefined; } return result.value; } tail(skipCount = 1) { return new StreamImpl(() => { const state = this.startFn(); for (let i = 0; i < skipCount; i++) { const next = this.nextFn(state); if (next.done) { return state; } } return state; }, this.nextFn); } limit(maxSize) { return new StreamImpl(() => ({ size: 0, state: this.startFn() }), state => { state.size++; if (state.size > maxSize) { return DONE_RESULT; } return this.nextFn(state.state); }); } distinct(by) { return new StreamImpl(() => ({ set: new Set(), internalState: this.startFn() }), state => { let result; do { result = this.nextFn(state.internalState); if (!result.done) { const value = by ? by(result.value) : result.value; if (!state.set.has(value)) { state.set.add(value); return result; } } } while (!result.done); return DONE_RESULT; }); } exclude(other, key) { const otherKeySet = new Set(); for (const item of other) { const value = key ? key(item) : item; otherKeySet.add(value); } return this.filter(e => { const ownKey = key ? key(e) : e; return !otherKeySet.has(ownKey); }); } } function toString(item) { if (typeof item === 'string') { return item; } if (typeof item === 'undefined') { return 'undefined'; } // eslint-disable-next-line @typescript-eslint/no-explicit-any if (typeof item.toString === 'function') { // eslint-disable-next-line @typescript-eslint/no-explicit-any return item.toString(); } return Object.prototype.toString.call(item); } function isIterable(obj) { return !!obj && typeof obj[Symbol.iterator] === 'function'; } /** * An empty stream of any type. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export const EMPTY_STREAM = new StreamImpl(() => undefined, () => DONE_RESULT); /** * Use this `IteratorResult` when implementing a `StreamImpl` to indicate that there are no more elements in the stream. */ export const DONE_RESULT = Object.freeze({ done: true, value: undefined }); /** * Create a stream from one or more iterables or array-likes. */ export function stream(...collections) { if (collections.length === 1) { const collection = collections[0]; if (collection instanceof StreamImpl) { return collection; } if (isIterable(collection)) { return new StreamImpl(() => collection[Symbol.iterator](), (iterator) => iterator.next()); } if (typeof collection.length === 'number') { return new StreamImpl(() => ({ index: 0 }), (state) => { if (state.index < collection.length) { return { done: false, value: collection[state.index++] }; } else { return DONE_RESULT; } }); } } if (collections.length > 1) { return new StreamImpl(() => ({ collIndex: 0, arrIndex: 0 }), (state) => { do { if (state.iterator) { const next = state.iterator.next(); if (!next.done) { return next; } state.iterator = undefined; } if (state.array) { if (state.arrIndex < state.array.length) { return { done: false, value: state.array[state.arrIndex++] }; } state.array = undefined; state.arrIndex = 0; } if (state.collIndex < collections.length) { const collection = collections[state.collIndex++]; if (isIterable(collection)) { state.iterator = collection[Symbol.iterator](); } else if (collection && typeof collection.length === 'number') { state.array = collection; } } } while (state.iterator || state.array || state.collIndex < collections.length); return DONE_RESULT; }); } return EMPTY_STREAM; } /** * The default implementation of `TreeStream` takes a root element and a function that computes the * children of its argument. Whether the root node included in the stream is controlled with the * `includeRoot` option, which defaults to `false`. */ export class TreeStreamImpl extends StreamImpl { constructor(root, children, options) { super(() => ({ iterators: (options === null || options === void 0 ? void 0 : options.includeRoot) ? [[root][Symbol.iterator]()] : [children(root)[Symbol.iterator]()], pruned: false }), state => { if (state.pruned) { state.iterators.pop(); state.pruned = false; } while (state.iterators.length > 0) { const iterator = state.iterators[state.iterators.length - 1]; const next = iterator.next(); if (next.done) { state.iterators.pop(); } else { state.iterators.push(children(next.value)[Symbol.iterator]()); return next; } } return DONE_RESULT; }); } iterator() { const iterator = { state: this.startFn(), next: () => this.nextFn(iterator.state), prune: () => { iterator.state.pruned = true; }, [Symbol.iterator]: () => iterator }; return iterator; } } /** * A set of utility functions that reduce a stream to a single value. */ export var Reduction; (function (Reduction) { /** * Compute the sum of a number stream. */ function sum(stream) { return stream.reduce((a, b) => a + b, 0); } Reduction.sum = sum; /** * Compute the product of a number stream. */ function product(stream) { return stream.reduce((a, b) => a * b, 0); } Reduction.product = product; /** * Compute the minimum of a number stream. Returns `undefined` if the stream is empty. */ function min(stream) { return stream.reduce((a, b) => Math.min(a, b)); } Reduction.min = min; /** * Compute the maximum of a number stream. Returns `undefined` if the stream is empty. */ function max(stream) { return stream.reduce((a, b) => Math.max(a, b)); } Reduction.max = max; })(Reduction || (Reduction = {})); //# sourceMappingURL=stream.js.map