UNPKG

@luvies/lazy

Version:

A linq-like lazy iteration module that aims to support deno, node & browser

800 lines 22.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LazyZipIterator = exports.LazyWhereIterator = exports.LazyUnionIterator = exports.LazyTakeWhileIterator = exports.LazyTakeLastIterator = exports.LazyTakeIterator = exports.LazySkipWhile = exports.LazySkipLastIterator = exports.LazySkipIterator = exports.LazySelectManyIterator = exports.LazySelectIterator = exports.LazyReverseIterator = exports.lazyOrderBy = exports.numericComparer = exports.LazyJoinIterator = exports.LazyIntersectIterator = exports.LazyGroupJoinIterator = exports.LazyGroupByIterator = exports.LazyExceptIterator = exports.LazyDistinctIterator = exports.LazyDefaultIfEmptyIterator = exports.LazyConcatIterator = exports.LazyBatchInIterator = exports.LazyAppendPrependIterator = exports.LazyRepeatIterator = exports.LazyRangeIterator = void 0; const aggregates_1 = require("./aggregates"); // Done helpers. /** * Always returns done: true. * @hidden */ function doneNext() { return { done: true, value: undefined }; } /** * Returns the standard done result, and sets the next function * on the current iterator to done that always returns done: true. * @remarks This will not modify the classes, since the next functions * on them are on the prototype, and this only modifies the current object. * @hidden */ function done(iterator) { iterator.next = doneNext; return doneNext(); } // Helper classes. /** * @hidden */ class Queue { constructor() { this._buffer = []; this._front = 0; } get length() { return this._buffer.length - this._front; } enqueue(element) { this._buffer.push(element); } dequeue() { if (this.length === 0) { throw new Error('Cannot dequeue an empty queue'); } const element = this._buffer[this._front]; // Memory & peformance enhancement thanks to // http://code.iamkate.com/javascript/queues/#implementation if (++this._front * 2 >= this._buffer.length) { this._buffer = this._buffer.slice(this._front); this._front = 0; } else if (this._front >= Queue._maxLeftoverCount) { // If the front is getting too large, start de-referencing items // so that we can start GC on them and don't build up too much // unused memory. this._buffer[this._front] = undefined; } return element; } } Queue._maxLeftoverCount = 10000; // Base Iterators /** * @hidden */ class LazyRangeIterator { constructor(_start, _end) { this._end = _end; this._index = _start; this._direction = _end < _start ? -1 : 1; } next() { if (this._direction > 0 ? this._index >= this._end : this._index <= this._end) { return done(this); } else { const nextResult = { done: false, value: this._index }; this._index += this._direction; return nextResult; } } } exports.LazyRangeIterator = LazyRangeIterator; /** * @hidden */ class LazyRepeatIterator { constructor(_element, _count) { this._element = _element; this._count = _count; this._index = 0; } next() { if (this._index >= this._count) { return done(this); } else { const nextResult = { done: false, value: this._element }; this._index++; return nextResult; } } } exports.LazyRepeatIterator = LazyRepeatIterator; // Iterators /** * @hidden */ class LazyAppendPrependIterator { constructor(iterable, _element, _atStart) { this._element = _element; this._atStart = _atStart; this._started = false; this._finished = false; this._iterator = iterable[Symbol.iterator](); } next() { if (this._finished) { return done(this); } if (!this._started) { this._started = true; if (this._atStart) { return { done: false, value: this._element }; } } const result = this._iterator.next(); if (result.done) { this._finished = true; if (!this._atStart) { return { done: false, value: this._element }; } else { return done(this); } } else { return result; } } } exports.LazyAppendPrependIterator = LazyAppendPrependIterator; /** * @hidden */ class LazyBatchInIterator { constructor(iterable, _batchSize, _includeIncomplete) { this._batchSize = _batchSize; this._includeIncomplete = _includeIncomplete; this._done = false; this._iterator = iterable[Symbol.iterator](); } next() { if (this._done) { return done(this); } const batch = []; while (true) { const result = this._iterator.next(); if (result.done) { this._done = true; if (batch.length > 0 && this._includeIncomplete) { return { done: false, value: batch }; } else { return done(this); } } else { batch.push(result.value); if (batch.length === this._batchSize) { return { done: false, value: batch }; } } } } } exports.LazyBatchInIterator = LazyBatchInIterator; /** * @hidden */ class LazyConcatIterator { constructor(iterables) { this._iterators = []; this._current = 0; for (const iterable of iterables) { this._iterators.push(iterable[Symbol.iterator]()); } } next() { if (this._current >= this._iterators.length) { return done(this); } while (this._current < this._iterators.length) { const result = this._iterators[this._current].next(); if (!result.done) { return result; } else { this._current++; } } return done(this); } } exports.LazyConcatIterator = LazyConcatIterator; /** * @hidden */ class LazyDefaultIfEmptyIterator { constructor(iterable, _defaultValue) { this._defaultValue = _defaultValue; this._started = false; this._done = false; this._iterator = iterable[Symbol.iterator](); } next() { if (this._done) { return done(this); } const result = this._iterator.next(); if (!this._started) { this._started = true; if (result.done) { this._done = true; return { done: false, value: this._defaultValue }; } else { return result; } } else { return result; } } } exports.LazyDefaultIfEmptyIterator = LazyDefaultIfEmptyIterator; /** * @hidden */ class LazyDistinctIterator { constructor(iterable, _compareOn) { this._compareOn = _compareOn; this._found = new Set(); this._iterator = iterable[Symbol.iterator](); } next() { while (true) { const result = this._iterator.next(); if (result.done) { return done(this); } else { const key = this._compareOn ? this._compareOn(result.value) : result.value; if (!this._found.has(key)) { this._found.add(key); return result; } } } } } exports.LazyDistinctIterator = LazyDistinctIterator; /** * @hidden */ class LazyExceptIterator { constructor(firstIterable, secondIterable, _compareOn) { this._compareOn = _compareOn; this._set = new Set(); this._firstIterator = firstIterable[Symbol.iterator](); for (const element of secondIterable) { this._set.add(_compareOn ? _compareOn(element) : element); } } next() { while (true) { const result = this._firstIterator.next(); if (result.done) { return done(this); } else { const key = this._compareOn ? this._compareOn(result.value) : result.value; if (!this._set.has(key)) { this._set.add(key); return result; } } } } } exports.LazyExceptIterator = LazyExceptIterator; /** * @hidden */ class LazyGroupByIterator { constructor(iterable, keyFn, elementSelector, _resultSelector) { this._resultSelector = _resultSelector; const elementMap = new Map(); for (const element of iterable) { const key = keyFn(element); const result = elementSelector ? elementSelector(element) : element; const arr = elementMap.get(key); if (!arr) { elementMap.set(key, [result]); } else { arr.push(result); } } this._elementMapIterator = elementMap[Symbol.iterator](); } next() { const result = this._elementMapIterator.next(); if (result.done) { return done(this); } else { const element = this._resultSelector ? this._resultSelector(result.value[0], result.value[1]) : { key: result.value[0], elements: result.value[1] }; return { done: false, value: element }; } } } exports.LazyGroupByIterator = LazyGroupByIterator; /** * @hidden */ class LazyGroupJoinIterator { constructor(firstIterable, secondIterable, _firstKeyFn, secondKeyFn, _joinFn) { this._firstKeyFn = _firstKeyFn; this._joinFn = _joinFn; this._secondMap = new Map(); this._firstIterator = firstIterable[Symbol.iterator](); for (const secondElement of secondIterable) { const key = secondKeyFn(secondElement); const arr = this._secondMap.get(key); if (!arr) { this._secondMap.set(key, [secondElement]); } else { arr.push(secondElement); } } } next() { while (true) { const result = this._firstIterator.next(); if (result.done) { return done(this); } else { const key = this._firstKeyFn(result.value); const secondElements = this._secondMap.get(key); if (secondElements) { return { done: false, value: this._joinFn(result.value, secondElements), }; } } } } } exports.LazyGroupJoinIterator = LazyGroupJoinIterator; /** * @hidden */ class LazyIntersectIterator { constructor(firstIterable, secondIterable, _compareOn) { this._compareOn = _compareOn; this._set = new Set(); this._firstIterator = firstIterable[Symbol.iterator](); for (const element of secondIterable) { const key = _compareOn ? _compareOn(element) : element; this._set.add(key); } } next() { while (true) { const result = this._firstIterator.next(); if (result.done) { return done(this); } else { const key = this._compareOn ? this._compareOn(result.value) : result.value; if (this._set.has(key)) { this._set.delete(key); return result; } } } } } exports.LazyIntersectIterator = LazyIntersectIterator; /** * @hidden */ class LazyJoinIterator { constructor(firstIterable, secondIterable, _firstKeyFn, secondKeyFn, _joinFn) { this._firstKeyFn = _firstKeyFn; this._joinFn = _joinFn; this._firstIterator = firstIterable[Symbol.iterator](); this._secondMap = aggregates_1.toMap(secondIterable, secondKeyFn); } next() { while (true) { const result = this._firstIterator.next(); if (result.done) { return done(this); } else { const key = this._firstKeyFn(result.value); const secondElement = this._secondMap.get(key); if (secondElement) { return { done: false, value: this._joinFn(result.value, secondElement), }; } } } } } exports.LazyJoinIterator = LazyJoinIterator; /** * Attempts to mimic the built-in sorting as close as possible. * @hidden */ function defaultComparer(a, b) { // eslint-disable-next-line @typescript-eslint/restrict-plus-operands return ('' + a).localeCompare('' + b); } /** * @hidden */ function numericComparer(a, b) { return a - b; } exports.numericComparer = numericComparer; /** * @hidden */ function comparerFactory(keyFn, reverse, compareFn = defaultComparer) { return (a, b) => { if (reverse) { const t = a; a = b; b = t; } return compareFn(keyFn(a), keyFn(b)); }; } /** * @hidden */ function lazyOrderBy(iterable, keyFn, compareFn, decending) { const arr = aggregates_1.toArray(iterable); arr.sort(comparerFactory(keyFn, decending, compareFn)); return arr[Symbol.iterator](); } exports.lazyOrderBy = lazyOrderBy; /** * @hidden */ class LazyReverseIterator { constructor(iterable) { this._arr = aggregates_1.toArray(iterable); this._index = this._arr.length - 1; } next() { if (this._index < 0) { return done(this); } else { const nextResult = { done: false, value: this._arr[this._index], }; this._index--; return nextResult; } } } exports.LazyReverseIterator = LazyReverseIterator; /** * @hidden */ class LazySelectIterator { constructor(iterable, _selector) { this._selector = _selector; this._index = 0; this._iterator = iterable[Symbol.iterator](); } next() { const result = this._iterator.next(); if (result.done) { return done(this); } else { const nextResult = { done: false, value: this._selector(result.value, this._index), }; this._index++; return nextResult; } } } exports.LazySelectIterator = LazySelectIterator; /** * @hidden */ class LazySelectManyIterator { constructor(iterable, _selector) { this._selector = _selector; this._index = 0; this._iterator = iterable[Symbol.iterator](); } next() { while (true) { if (!this._internalIterator) { const result = this._iterator.next(); if (result.done) { return done(this); } else { const element = this._selector(result.value, this._index); this._index++; this._internalIterator = element[Symbol.iterator](); } } const internalResult = this._internalIterator.next(); if (internalResult.done) { this._internalIterator = undefined; } else { return internalResult; } } } } exports.LazySelectManyIterator = LazySelectManyIterator; /** * @hidden */ class LazySkipIterator { constructor(iterable, _count) { this._count = _count; this._skipped = 0; this._iterator = iterable[Symbol.iterator](); } next() { while (true) { const result = this._iterator.next(); if (result.done) { return done(this); } else { if (this._skipped < this._count) { this._skipped++; } else { return result; } } } } } exports.LazySkipIterator = LazySkipIterator; /** * @hidden */ class LazySkipLastIterator { constructor(iterable, _count) { this._count = _count; this._queue = new Queue(); this._iterator = iterable[Symbol.iterator](); } next() { while (true) { const result = this._iterator.next(); if (result.done) { return done(this); } else { this._queue.enqueue(result.value); if (this._queue.length > this._count) { return { done: false, value: this._queue.dequeue() }; } } } } } exports.LazySkipLastIterator = LazySkipLastIterator; /** * @hidden */ class LazySkipWhile { constructor(iterable, _predicate) { this._predicate = _predicate; this._index = 0; this._yielding = false; this._iterator = iterable[Symbol.iterator](); } next() { while (true) { const result = this._iterator.next(); if (result.done) { return done(this); } else { if (!this._yielding) { this._yielding = !this._predicate(result.value, this._index); this._index++; } if (this._yielding) { return result; } } } } } exports.LazySkipWhile = LazySkipWhile; /** * @hidden */ class LazyTakeIterator { constructor(iterable, _count) { this._count = _count; this._taken = 0; this._iterator = iterable[Symbol.iterator](); } next() { if (this._taken >= this._count) { return done(this); } const result = this._iterator.next(); this._taken++; if (result.done) { return done(this); } else { return result; } } } exports.LazyTakeIterator = LazyTakeIterator; /** * @hidden */ class LazyTakeLastIterator { constructor(iterable, count) { this._queue = new Queue(); if (count > 0) { for (const element of iterable) { if (this._queue.length < count) { this._queue.enqueue(element); } else { this._queue.dequeue(); this._queue.enqueue(element); } } } } next() { if (this._queue.length === 0) { return done(this); } else { return { done: false, value: this._queue.dequeue() }; } } } exports.LazyTakeLastIterator = LazyTakeLastIterator; /** * @hidden */ class LazyTakeWhileIterator { constructor(iterable, _predicate) { this._predicate = _predicate; this._index = 0; this._iterator = iterable[Symbol.iterator](); } next() { const result = this._iterator.next(); if (result.done) { return done(this); } else { if (!this._predicate(result.value, this._index)) { return done(this); } else { this._index++; return result; } } } } exports.LazyTakeWhileIterator = LazyTakeWhileIterator; /** * @hidden */ class LazyUnionIterator { constructor(firstIterable, secondIterable, _compareOn) { this._compareOn = _compareOn; this._set = new Set(); this._onSecond = false; this._firstIterator = firstIterable[Symbol.iterator](); this._secondIterator = secondIterable[Symbol.iterator](); } next() { while (true) { const result = this._onSecond ? this._secondIterator.next() : this._firstIterator.next(); if (result.done) { if (this._onSecond) { return done(this); } else { this._onSecond = true; } } else { const key = this._compareOn ? this._compareOn(result.value) : result.value; if (!this._set.has(key)) { this._set.add(key); return { done: false, value: result.value }; } } } } } exports.LazyUnionIterator = LazyUnionIterator; /** * @hidden */ class LazyWhereIterator { constructor(iterable, _predicate) { this._predicate = _predicate; this._index = 0; this._iterator = iterable[Symbol.iterator](); } next() { while (true) { const result = this._iterator.next(); if (result.done) { return done(this); } else { const shouldYield = this._predicate(result.value, this._index); this._index++; if (shouldYield) { return { done: false, value: result.value }; } } } } } exports.LazyWhereIterator = LazyWhereIterator; /** * @hidden */ class LazyZipIterator { constructor(firstIterable, secondIterable, _selector) { this._selector = _selector; this._firstIterator = firstIterable[Symbol.iterator](); this._secondIterator = secondIterable[Symbol.iterator](); } next() { const firstResult = this._firstIterator.next(); if (firstResult.done) { return done(this); } const secondResult = this._secondIterator.next(); if (secondResult.done) { return done(this); } return { done: false, value: this._selector ? this._selector(firstResult.value, secondResult.value) : [firstResult.value, secondResult.value], }; } } exports.LazyZipIterator = LazyZipIterator; //# sourceMappingURL=iterators.js.map