iteragain
Version:
Javascript Iterable/Iterator/Generator-function utilities.
618 lines • 28.7 kB
JavaScript
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
import toIterator from '../toIterator';
import ConcatIterator from './ConcatIterator';
import FilterIterator from './FilterIterator';
import FlattenIterator from './FlattenIterator';
import MapIterator from './MapIterator';
import WindowsIterator from './WindowsIterator';
import PairwiseIterator from './PairwiseIterator';
import SliceIterator from './SliceIterator';
import ZipIterator from './ZipIterator';
import ZipLongestIterator from './ZipLongestIterator';
import TapIterator from './TapIterator';
import TriplewiseIterator from './TriplewiseIterator';
import ChunksIterator from './ChunksIterator';
import TakeWhileIterator from './TakeWhileIterator';
import CycleIterator from './CycleIterator';
import ResumeIterator from './ResumeIterator';
import PermutationsIterator from './PermutationsIterator';
import FilterMapIterator from './FilterMapIterator';
import DropWhileIterator from './DropWhileIterator';
import CompressIterator from './CompressIterator';
import ProductIterator from './ProductIterator';
import CombinationsIterator from './CombinationsIterator';
import SeekableIterator from './SeekableIterator';
import TeedIterator from './TeedIterator';
import count from '../count';
import FlatMapIterator from './FlatMapIterator';
import GroupByIterator from './GroupByIterator';
var enumerator = function (count) {
if (count === void 0) { count = 0; }
return function (v) { return [count++, v]; };
};
/**
* Extends and implements the IterableIterator interface. Methods marked with the `@lazy` prefix are chainable methods
* that modify the internal iterator, but don't start iterating. Methods without the `@lazy` prefix do start iterating
* some amount, depending on the method.
*/
var ExtendedIterator = /** @class */ (function () {
function ExtendedIterator(iterator) {
this.iterator = iterator;
}
/** Returns a `{ value, done }` object that adheres to the Iterator interface. */
ExtendedIterator.prototype.next = function () {
var _a;
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return (_a = this.iterator).next.apply(_a, args);
};
/** Implements this as an Iterable so it's allowed to be used with "for of" loops. */
ExtendedIterator.prototype[Symbol.iterator] = function () {
return this;
};
ExtendedIterator.prototype.toString = function () {
return 'ExtendedIterator';
};
/** @lazy Returns a new ExtendedIterator that maps each element in this iterator to a new value. */
ExtendedIterator.prototype.map = function (iteratee) {
return new ExtendedIterator(new MapIterator(this.iterator, iteratee));
};
ExtendedIterator.prototype.filter = function (predicate) {
return new ExtendedIterator(new FilterIterator(this.iterator, predicate));
};
/**
* @lazy
* Maps and filters the input iterator in the same `iteratee` function.
* @param iteratee A function that maps each value in this iterator to a new value and also filters out any that
* return a nullish value.
*/
ExtendedIterator.prototype.filterMap = function (iteratee) {
return new ExtendedIterator(new FilterMapIterator(this.iterator, iteratee));
};
/** @lazy Concatenates this iterator with the given iterators, in order of: `[this.iterator, ...others]`. */
ExtendedIterator.prototype.concat = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return new ExtendedIterator(new ConcatIterator(__spreadArray([this.iterator], args.map(toIterator), true)));
};
/** @lazy Prepends this iterator with the given iterators, in order of: `[...args, this.iterator]`. */
ExtendedIterator.prototype.prepend = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return new ExtendedIterator(new ConcatIterator(__spreadArray(__spreadArray([], args.map(toIterator), true), [this.iterator], false)));
};
/**
* @lazy
* Works like `Array.prototype.slice`, returns a new slice of this iterator.
* @note This does not support negative `start` and `end` indices, as it's not possible to know the length of the
* iterator while iterating.
* @param start The index to start at (inclusive).
* @param end The index to end at (exclusive).
* @returns A new ExtendedIterator that only includes the elements between `start` and `end`.
*/
ExtendedIterator.prototype.slice = function (start, end) {
if (start === void 0) { start = 0; }
if (end === void 0) { end = Infinity; }
return new ExtendedIterator(new SliceIterator(this.iterator, start, end));
};
ExtendedIterator.prototype.flatten = function (depth) {
if (depth === void 0) { depth = Infinity; }
return new ExtendedIterator(new FlattenIterator(this.iterator, depth));
};
/** @lazy Attaches the index to each value as a pair like: `[0, value], [1, value]`, etc. */
ExtendedIterator.prototype.enumerate = function () {
return this.map(enumerator());
};
/**
* @lazy
* The inverse of `zip` and `zipLongest`. This method disaggregates the elements of this iterator. The nth iterator
* in the returned tuple contains the nth element of each value in this iterator. The length of the returned tuple is
* determined by the length of the first value in this iterator.
*/
ExtendedIterator.prototype.unzip = function () {
var head = this.peek()[0];
var n = Array.isArray(head) ? head.length : 1;
if (n < 2)
return [this];
return this.tee(n).map(function (it, i) { return it.map(function (v) { return v[i]; }); });
};
ExtendedIterator.prototype.zip = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return new ExtendedIterator(new ZipIterator(__spreadArray([this.iterator], args.map(toIterator), true)));
};
ExtendedIterator.prototype.zipLongest = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return new ExtendedIterator(new ZipLongestIterator(__spreadArray([this.iterator], args.map(toIterator), true)));
};
/**
* @lazy
* Return a new iterator of pairs (tuples) of the values in this one. The number of pairs will always be one fewer
* than this iterator. Will be empty if this iterator has fewer than two values.
* @example
* iter([1,2,3]).pairwise().toArray() // [[1,2], [2,3]]
* iter([1]).pairwise().toArray() // []
*/
ExtendedIterator.prototype.pairwise = function () {
return new ExtendedIterator(new PairwiseIterator(this.iterator));
};
/**
* @lazy
* Returns a new iterator of triplets (tuples) of the values in this one. The number of triplets will always be two
* fewer than the number of values in this iterator. Will be empty if this iterator has fewer than three values.
*/
ExtendedIterator.prototype.triplewise = function () {
return new ExtendedIterator(new TriplewiseIterator(this.iterator));
};
/**
* @lazy
* Take all elements from this iterator while the given `predicate` returns a truthy value.
* @param predicate A function to call for each value.
*/
ExtendedIterator.prototype.takeWhile = function (predicate) {
return new ExtendedIterator(new TakeWhileIterator(this.iterator, predicate));
};
/**
* @lazy
* Drop/skip values in this iterator while the passed `predicate` returns a truthy value.
* @param predicate The function to call for each value.
*/
ExtendedIterator.prototype.dropWhile = function (predicate) {
return new ExtendedIterator(new DropWhileIterator(this.iterator, predicate));
};
/**
* @lazy
* Tap into this iterator by supplying `func` which is passed each value of this iterator. The return value of
* func is unused and this method is purely designed for a designated place to perform side effects.
* @example
* iter([1,2,3])
* .tap(console.log) // logs 1, 2, 3 to the console
* .map(n => n * n)
* .tap(console.log) // logs 1, 4, 9 to the console
* .toArray() // returns [1, 4, 9]
*/
ExtendedIterator.prototype.tap = function (func) {
return new ExtendedIterator(new TapIterator(this.iterator, func));
};
/**
* @lazy
* Yields non-overlapping chunks (tuples) of `length` from this iterator.
* @param length The length of each chunk, must be greater than 0.
* @param fill Optional, the value to fill the last chunk with if it's not the same length as the rest of the iterator.
* @example
* iter([1,2,3,4,5,6,7,8,9]).chunk(3).toArray() // [[1,2,3], [4,5,6], [7,8,9]]
* iter([1,2,3,4,5,6,7,8,9]).chunk(2, 0).toArray() // [[1,2], [3,4], [5,6], [7,8], [9, 0]]
*/
ExtendedIterator.prototype.chunks = function (length, fill) {
return new ExtendedIterator(new ChunksIterator(this.iterator, length, fill));
};
/**
* @lazy
* Yields sliding windows (tuples) of `length` from this iterator. Each window is separated by `offset` number of
* elements.
* @param length The length of each window, must be greater than 0.
* @param offset The offset of each window from each other. Must be greater than 0.
* @param fill Optional, the value to fill the last window with if it's not the same length as the rest of the iterator.
* @example
* iter([1,2,3,4,5]).windows(2, 1).toArray() // [[1,2], [2,3], [3,4], [4,5]]
* iter([1,2,3,4,5]).windows(2, 3).toArray() // [[1,2], [4,5]]
* iter([1,2,3,4,5]).windows(3, 3, 0).toArray() // [[1,2,3], [4,5,0]]
*/
ExtendedIterator.prototype.windows = function (length, offset, fill) {
return new ExtendedIterator(new WindowsIterator(this.iterator, length, offset, fill));
};
/**
* @lazy
* Returns `n` independent iterators, each of which is a copy of this iterator at the time of calling `tee`. Once
* `tee` has made a split, do not modify or call upon the original iterator, as the new iterators will not be
* updated/informed.
* This caches the original iterator's values as the new iterators are iterated through. So
* depending on the size of the original iterator, there could be significant memory overhead in using `tee`.
* `tee`'s intended use is to iterate over the returned iterators in parallel, or at least somewhat in parallel. In
* general, if one returned iterator consumes most or all of it's values, then it is faster to just
* use `toArray` and then iterate over that.
* @param n The number of independent iterators to create.
*/
ExtendedIterator.prototype.tee = function (n) {
var seekable = new SeekableIterator(toIterator(this.iterator));
var indices = new Array(n).fill(0);
return Array.from({ length: n }, function (_, i) { return new ExtendedIterator(new TeedIterator(i, seekable, indices)); });
};
/**
* @lazy
* Makes this iterator cycle infinitely through it's values.
* @param times The number of times to cycle through the iterator (default: Infinity).
* @example
* equal(iter([1,2,3]).cycle().take(5).toArray(), [1,2,3,1,2])
*/
ExtendedIterator.prototype.cycle = function (times) {
if (times === void 0) { times = Infinity; }
return new ExtendedIterator(new CycleIterator(this.iterator, times));
};
/**
* @lazy
* Resumes this iterator a certain number of times after it's next value returns `{ done: true }`.
* @param times The number of times to resume the iterator (default: Infinity).
* @example
* const it = iter([1,2,3]).resume(1);
* equal(it.toArray(), [1,2,3]);
* equal(it.toArray(), [1,2,3]);
* equal(it.toArray(), []);
*/
ExtendedIterator.prototype.resume = function (times) {
if (times === void 0) { times = Infinity; }
return new ExtendedIterator(new ResumeIterator(this.iterator, times));
};
/**
* @lazy
* Filters/compresses this iterator to only values that correspond to truthy values in `selectors`.
* @param selectors An iterator or iterable of falsey or truthy values to select which values to keep in this
* iterator.
*/
ExtendedIterator.prototype.compress = function (selectors) {
return new ExtendedIterator(new CompressIterator(this.iterator, toIterator(selectors)));
};
/**
* @lazy
* Returns all successive `size` length permutations of this iterator. The permutations are emitted in lexicographic
* ordering according to this iterator. So if this iterator is sorted, the permutations will be in sorted order.
* Elements in the permutations are treated as unique based on their position in the iterator, not on their value. So
* if the input iterator is unique, then there will be no repeat values.
* @see https://docs.python.org/3/library/itertools.html#itertools.permutations for more info.
* @param size The size of each permutation, must be greater than 0 and less than or equal to the length of this
* iterator.
*/
ExtendedIterator.prototype.permutations = function (size) {
return new ExtendedIterator(new PermutationsIterator(this.iterator, size));
};
/**
* @lazy
* Returns `size` length subsequences of this iterator.
* @see https://docs.python.org/3/library/itertools.html#itertools.combinations for more info.
* @see https://docs.python.org/3/library/itertools.html#itertools.combinations_with_replacement for more info.
* @param size The size of each combination.
* @param withReplacement Whether or not to allow duplicate elements in the combinations.
*/
ExtendedIterator.prototype.combinations = function (size, withReplacement) {
if (withReplacement === void 0) { withReplacement = false; }
return new ExtendedIterator(new CombinationsIterator(this.iterator, size, withReplacement));
};
ExtendedIterator.prototype.product = function () {
var _a;
var params = [];
for (var _i = 0; _i < arguments.length; _i++) {
params[_i] = arguments[_i];
}
var iterators = typeof params[0] === 'number' ? [] : params[0];
var repeat = (_a = params.find(function (param) { return typeof param === 'number'; })) !== null && _a !== void 0 ? _a : 1;
return new ExtendedIterator(new ProductIterator(__spreadArray([this.iterator], iterators.map(toIterator), true), repeat));
};
/**
* @lazy
* Filters this iterator to only unique values.
* @param iteratee Iteratee to use to transform each value before being tested for uniqueness.
* @param justSeen If true, will only test for uniqueness with the last value in the iterator and not all values.
*/
ExtendedIterator.prototype.unique = function (params) {
if (params === void 0) { params = function (v) { return v; }; }
var _a = typeof params === 'function' ? { iteratee: params } : params, _b = _a.iteratee, iteratee = _b === void 0 ? function (v) { return v; } : _b, _c = _a.justSeen, justSeen = _c === void 0 ? false : _c;
if (justSeen) {
var lastValue_1;
return this.filter(function (value) {
value = iteratee(value);
if (!lastValue_1 || value !== lastValue_1) {
lastValue_1 = value;
return true;
}
return false;
});
}
var seen = new Set();
return this.filter(function (value) {
value = iteratee(value);
if (!seen.has(value)) {
seen.add(value);
return true;
}
return false;
});
};
/**
* @lazy
* Reverses this iterator's order. Note that in order to reverse, it will attempt to iterate fully once, which
* could cause significant memory usage. So because of this, only use on finite iterators.
*/
ExtendedIterator.prototype.reverse = function () {
var next;
var result = [];
while (!(next = this.iterator.next()).done)
result.unshift(next.value);
return new ExtendedIterator(toIterator(result));
};
/** @lazy Maps `key` from `T` in each value of this iterator. */
ExtendedIterator.prototype.pluck = function (key) {
return this.filterMap(function (value) { return value[key]; });
};
ExtendedIterator.prototype.reduce = function (reducer, initialValue) {
var next;
var accumulator = initialValue !== null && initialValue !== void 0 ? initialValue : this.iterator.next().value;
while (!(next = this.iterator.next()).done)
accumulator = reducer(accumulator, next.value);
return accumulator;
};
/** Consumes this iterator and returns the number of values/items in it. */
ExtendedIterator.prototype.length = function () {
return this.reduce(function (acc, _) { return acc + 1; }, 0);
};
/** Returns the number of times the `predicate` returns a truthy value. */
ExtendedIterator.prototype.quantify = function (predicate) {
return this.reduce(function (acc, v) { return acc + (predicate(v) ? 1 : 0); }, 0);
};
/** Returns the minimum value from this iterator. */
ExtendedIterator.prototype.min = function (iteratee) {
if (iteratee === void 0) { iteratee = function (v) { return v; }; }
var next = this.iterator.next();
var min = { value: next.value, comparison: iteratee(next.value) };
while (!(next = this.iterator.next()).done) {
var comparison = iteratee(next.value);
if (comparison < min.comparison)
min = { value: next.value, comparison: comparison };
}
return min.value;
};
/** Returns the maximum value from this iterator. */
ExtendedIterator.prototype.max = function (iteratee) {
if (iteratee === void 0) { iteratee = function (v) { return v; }; }
var next = this.iterator.next();
var max = { value: next.value, comparison: iteratee(next.value) };
while (!(next = this.iterator.next()).done) {
var comparison = iteratee(next.value);
if (comparison > max.comparison)
max = { value: next.value, comparison: comparison };
}
return max.value;
};
/** Returns the minimum and maximum from this iterator as a tuple: `[min, max]`. */
ExtendedIterator.prototype.minmax = function (iteratee) {
if (iteratee === void 0) { iteratee = function (v) { return v; }; }
var next = this.iterator.next();
var min = { value: next.value, comparison: iteratee(next.value) };
var max = { value: next.value, comparison: iteratee(next.value) };
while (!(next = this.iterator.next()).done) {
var comparison = iteratee(next.value);
if (comparison < min.comparison)
min = { value: next.value, comparison: comparison };
if (comparison > max.comparison)
max = { value: next.value, comparison: comparison };
}
return [min.value, max.value];
};
/** Iterate over this iterator using the `array.prototype.forEach` style of method. */
ExtendedIterator.prototype.forEach = function (callback) {
var next;
while (!(next = this.iterator.next()).done)
callback(next.value);
};
/** Return true if every element in this iterator matches the predicate. */
ExtendedIterator.prototype.every = function (predicate) {
var next;
while (!(next = this.iterator.next()).done)
if (!predicate(next.value))
return false;
return true;
};
/** Return true if only one element in this iterator matches the predicate. */
ExtendedIterator.prototype.some = function (predicate) {
var next;
while (!(next = this.iterator.next()).done)
if (predicate(next.value))
return true;
return false;
};
/**
* Returns this iterator as a string with each value joined by `separator`.
* @param separator The separator to use between each value (default: ',').
*/
ExtendedIterator.prototype.join = function (separator) {
var _a;
if (separator === void 0) { separator = ','; }
return ((_a = this.reduce(function (str, v) { return (str + separator + v); })) !== null && _a !== void 0 ? _a : '');
};
ExtendedIterator.prototype.find = function (predicate) {
var next;
while (!(next = this.iterator.next()).done)
if (predicate(next.value))
return next.value;
};
/**
* Finds the index of the first value that passes a truthy vale to `predicate`, then returns it. Only consumes the
* iterator's values up to the found value, then stops. So if it's not found, then the iterator is exhausted.
*/
ExtendedIterator.prototype.findIndex = function (predicate) {
var next;
var i = -1;
while ((i++, !(next = this.iterator.next()).done))
if (predicate(next.value))
return i;
return -1;
};
/**
* Maps this iterator to a new value `R` and flattens any resulting iterables or iterators by a depth of 1.
* Behaves the same as `Array.prototype.flatMap`.
*/
ExtendedIterator.prototype.flatMap = function (iteratee) {
return new ExtendedIterator(new FlatMapIterator(this.iterator, iteratee));
};
/** Returns true if `value` strictly equals some value in this iterator. */
ExtendedIterator.prototype.includes = function (value) {
return this.some(function (v) { return v === value; });
};
/**
* Peek ahead of where the current iteration is. This doesn't consume any values of the iterator.
* @param ahead optional, the number of elements to peek ahead.
*/
ExtendedIterator.prototype.peek = function (ahead) {
if (ahead === void 0) { ahead = 1; }
var values = this.take(ahead);
if (values.length)
this.iterator = new ConcatIterator([toIterator(values), this.iterator]);
return values;
};
/**
* Take `n` number of values from this iterator.
* @param n The number of values to take.
*/
ExtendedIterator.prototype.take = function (n) {
if (n === void 0) { n = 1; }
var values = [];
var next;
while (n-- > 0 && !(next = this.iterator.next()).done)
values.push(next.value);
return values;
};
/**
* @deprecated Use `consume` instead, as this is the more standard name for this type of method.
* Start iterating through this iterator, but don't return the values from this method.
* @param n optional, the number of elements to exhaust.
*/
ExtendedIterator.prototype.exhaust = function (n) {
if (n === void 0) { n = Infinity; }
this.consume(n);
};
/**
* Start iterating through this iterator, but don't return the values from this method.
* @param n optional, the number of elements to consume (default: Infinity).
*/
ExtendedIterator.prototype.consume = function (n) {
if (n === void 0) { n = Infinity; }
while (n-- > 0 && !this.iterator.next().done)
;
};
/**
* Collects all values from this iterator, then shuffles the order of it's values.
* @param seed A seed between 0 and 1.
*/
ExtendedIterator.prototype.shuffle = function (seed) {
var _a;
if (seed === void 0) { seed = Math.random(); }
var values = this.toArray();
for (var i = values.length - 1; i > 0; i--) {
var j = Math.floor(seed * (i + 1));
_a = [values[j], values[i]], values[i] = _a[0], values[j] = _a[1];
}
return new ExtendedIterator(toIterator(values));
};
/** Collects all values from this iterator, then sorts them. */
ExtendedIterator.prototype.sort = function (comparator) {
if (comparator === void 0) { comparator = function (a, b) { return (a < b ? -1 : a > b ? 1 : 0); }; }
return new ExtendedIterator(toIterator(this.toArray().sort(comparator)));
};
/**
* Partitions this iterator into a tuple of `[falsey, truthy]` corresponding to what `predicate` returns for each
* value.
*/
ExtendedIterator.prototype.partition = function (predicate) {
var falsey = [];
var truthy = [];
var next;
while (!(next = this.iterator.next()).done) {
if (predicate(next.value))
truthy.push(next.value);
else
falsey.push(next.value);
}
return [falsey, truthy];
};
/**
* @lazy
* Distributes this iterator's values among `n` amount of smaller iterators. Does not maintain order so if order is
* important, use `divide` instead.
*/
ExtendedIterator.prototype.distribute = function (n) {
return this.tee(n).map(function (it, i) { return it.compress(new ExtendedIterator(count()).map(function (v) { return (v - i) % n === 0; })); });
};
ExtendedIterator.prototype.groupBy = function (key) {
if (key === void 0) { key = function (v) { return v; }; }
return new ExtendedIterator(new GroupByIterator(this.iterator, key));
};
/**
* Divides this iterator into `n` amount of smaller iterators while maintaining order. Note, this method will fully
* iterate through this iterator before returning a result. If you don't want this behavior and don't care about
* order then use `distribute` instead.
*/
ExtendedIterator.prototype.divide = function (n) {
var array = this.toArray();
var result = [];
var quotient = Math.floor(array.length / n);
var remainder = array.length % n;
var stop = 0;
var start = 0;
for (var i = 1; i < n + 1; i++) {
start = stop;
stop += i <= remainder ? quotient + 1 : quotient;
result.push(new ExtendedIterator(toIterator(array.slice(start, stop))));
}
return result;
};
/**
* Iterates and finds the element at `index`. Returns undefined if not found.
* @param index The index to find. Only supports positive indices.
*/
ExtendedIterator.prototype.nth = function (index) {
var i = 0;
var next;
while (!(next = this.iterator.next()).done && index !== i++)
;
return next.value;
};
/** Iterates and collects all values into an Array. */
ExtendedIterator.prototype.toArray = function () {
var result = [];
var next;
while (!(next = this.iterator.next()).done)
result.push(next.value);
return result;
};
/** Calls `Promise.all` on all collected values. */
ExtendedIterator.prototype.promiseAll = function () {
return Promise.all(this.toArray());
};
/** Calls `Promise.race` on all collected values. */
ExtendedIterator.prototype.promiseRace = function () {
return Promise.race(this.toArray());
};
/** Shorthand for `new Set(this)`. */
ExtendedIterator.prototype.toSet = function () {
return new Set(this);
};
/**
* Shorthand for `new Map<K, V>(this)`. The type of this iterator must extend `any[]` for this to work. And you may
* also need to pass in your own values for the generics: e.g. `iterator.toMap<string, number>();`
*/
ExtendedIterator.prototype.toMap = function () {
return new Map(this);
};
return ExtendedIterator;
}());
export { ExtendedIterator };
export default ExtendedIterator;
//# sourceMappingURL=ExtendedIterator.js.map