151 lines
3.3 KiB
JavaScript
151 lines
3.3 KiB
JavaScript
// LINQ-like Enumerable class wrapping lazy generator chains
|
|
|
|
class Enumerable {
|
|
constructor(iteratorFn) {
|
|
this._iteratorFn = iteratorFn;
|
|
}
|
|
|
|
[Symbol.iterator]() {
|
|
return this._iteratorFn();
|
|
}
|
|
|
|
_chain(generatorFn) {
|
|
const source = this;
|
|
return new Enumerable(function* () {
|
|
yield* generatorFn(source);
|
|
});
|
|
}
|
|
|
|
where(predicate) {
|
|
return this._chain(function* (source) {
|
|
for (const item of source) if (predicate(item)) yield item;
|
|
});
|
|
}
|
|
|
|
select(selector) {
|
|
return this._chain(function* (source) {
|
|
for (const item of source) yield selector(item);
|
|
});
|
|
}
|
|
|
|
take(count) {
|
|
count = Math.max(0, count);
|
|
return this._chain(function* (source) {
|
|
for (const item of source) {
|
|
if (count-- <= 0) break;
|
|
yield item;
|
|
}
|
|
});
|
|
}
|
|
|
|
skip(count) {
|
|
count = Math.max(0, count);
|
|
return this._chain(function* (source) {
|
|
for (const item of source) {
|
|
if (count-- > 0) continue;
|
|
yield item;
|
|
}
|
|
});
|
|
}
|
|
|
|
isLast() {
|
|
return this._chain(function* (source) {
|
|
const iter = source[Symbol.iterator]();
|
|
let current = iter.next();
|
|
let index = 0;
|
|
while (!current.done) {
|
|
const next = iter.next();
|
|
yield [current.value, next.done, index];
|
|
current = next;
|
|
index++;
|
|
}
|
|
});
|
|
}
|
|
|
|
forEach(action) {
|
|
for (const item of this) {
|
|
if (Array.isArray(item)) {
|
|
action(...item);
|
|
} else {
|
|
action(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
toArray() {
|
|
return Array.from(this);
|
|
}
|
|
|
|
orderByDescending(keySelector) {
|
|
return this._chain(function* (source) {
|
|
const items = Array.from(source);
|
|
items.sort((a, b) => keySelector(b) - keySelector(a));
|
|
yield* items;
|
|
});
|
|
}
|
|
|
|
orderBy(keySelector) {
|
|
return this._chain(function* (source) {
|
|
const items = Array.from(source);
|
|
items.sort((a, b) => keySelector(a) - keySelector(b));
|
|
yield* items;
|
|
});
|
|
}
|
|
|
|
firstOrDefault(predicate) {
|
|
const source = predicate ? this.where(predicate) : this;
|
|
for (const item of source) return item;
|
|
return undefined;
|
|
}
|
|
|
|
first(predicate) {
|
|
const source = predicate ? this.where(predicate) : this;
|
|
for (const item of source) return item;
|
|
throw new Error("No elements in sequence.");
|
|
}
|
|
|
|
lastOrDefault(predicate) {
|
|
const source = predicate ? this.where(predicate) : this;
|
|
let lastValue = undefined;
|
|
for (const item of source) lastValue = item;
|
|
return lastValue;
|
|
}
|
|
|
|
last(predicate) {
|
|
const source = predicate ? this.where(predicate) : this;
|
|
let lastValue = undefined;
|
|
let found = false;
|
|
for (const item of source) {
|
|
lastValue = item;
|
|
found = true;
|
|
}
|
|
if (!found) throw new Error("No elements in sequence.");
|
|
return lastValue;
|
|
}
|
|
|
|
any(predicate) {
|
|
const source = predicate ? this.where(predicate) : this;
|
|
for (const _ of source) return true;
|
|
return false;
|
|
}
|
|
|
|
all(predicate) {
|
|
for (const item of this) if (!predicate(item)) return false;
|
|
return true;
|
|
}
|
|
|
|
count(predicate) {
|
|
let count = 0;
|
|
const source = predicate ? this.where(predicate) : this;
|
|
for (const _ of source) count++;
|
|
return count;
|
|
}
|
|
}
|
|
|
|
Array.prototype.asEnumerable = function () {
|
|
const arr = this;
|
|
return new Enumerable(function* () {
|
|
for (const item of arr) yield item;
|
|
});
|
|
};
|