@luvies/lazy
Version:
A linq-like lazy iteration module that aims to support deno, node & browser
800 lines • 22.8 kB
JavaScript
"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