200 lines
4.2 KiB
JavaScript
200 lines
4.2 KiB
JavaScript
'use strict';
|
|
|
|
function identity(x) {
|
|
return x;
|
|
}
|
|
|
|
export class Iterator {
|
|
constructor(from) {
|
|
let next;
|
|
|
|
if (Symbol.iterator in from.__proto__) {
|
|
const iter = from.__proto__[Symbol.iterator].call(from);
|
|
next = iter.next.bind(iter);
|
|
} else if (typeof from === 'function') {
|
|
next = from;
|
|
} else {
|
|
throw new TypeError('Iterator class needs iterable input');
|
|
}
|
|
|
|
Object.defineProperty(this, 'next', {
|
|
value: next,
|
|
writable: false,
|
|
enumerable: true,
|
|
configurable: false,
|
|
});
|
|
}
|
|
|
|
[Symbol.iterator]() {
|
|
return this;
|
|
}
|
|
|
|
map(f) {
|
|
const next = this.next;
|
|
return new Iterator(function() {
|
|
const y = next();
|
|
if (y.done) {
|
|
return y;
|
|
} else {
|
|
return { value: f(y.value), done: false };
|
|
}
|
|
});
|
|
}
|
|
|
|
filter(f) {
|
|
const next = this.next;
|
|
return new Iterator(function() {
|
|
let y;
|
|
while (!(y = next()).done && !f(y.value)) {
|
|
continue;
|
|
}
|
|
return y;
|
|
});
|
|
}
|
|
|
|
take(limit) {
|
|
let remaining = Number(limit) & -1;
|
|
const next = this.next;
|
|
return new Iterator(function() {
|
|
if (remaining > 0) {
|
|
remaining -= 1;
|
|
return next();
|
|
} else {
|
|
return { value: undefined, done: true };
|
|
}
|
|
});
|
|
}
|
|
|
|
drop(limit) {
|
|
let remaining = Number(limit) & -1;
|
|
const next = this.next;
|
|
return new Iterator(function() {
|
|
while (remaining > 0 && !next().done) {
|
|
remaining -= 1;
|
|
}
|
|
return next();
|
|
});
|
|
}
|
|
|
|
asIndexedPairs() {
|
|
const next = this.next;
|
|
let index = 0;
|
|
return new Iterator(function() {
|
|
const y = next();
|
|
if (y.done) {
|
|
return y;
|
|
} else {
|
|
return { value: [index++, y.value], done: false };
|
|
}
|
|
});
|
|
}
|
|
|
|
flatMap(f) {
|
|
const next = this.next;
|
|
let innerNext;
|
|
|
|
return new Iterator(function() {
|
|
for (;;) {
|
|
if (innerNext) {
|
|
let y = innerNext();
|
|
if (!y.done) {
|
|
return y;
|
|
}
|
|
}
|
|
|
|
let z = next();
|
|
if (z.done) {
|
|
innerNext = undefined;
|
|
return z;
|
|
}
|
|
|
|
const iter = y.value.__proto__[Symbol.iterator].call(y.value);
|
|
innerNext = iter.next.bind(iter);
|
|
}
|
|
});
|
|
}
|
|
|
|
reduce(f, state) {
|
|
if (typeof state === 'undefined') {
|
|
const first = this.next();
|
|
if (first.done) {
|
|
throw new TypeError('reduce: empty iterator');
|
|
}
|
|
state = first.value;
|
|
}
|
|
|
|
let y;
|
|
while (!(y = this.next()).done) {
|
|
state = f(state, y.value);
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
toArray() {
|
|
return [...this];
|
|
}
|
|
|
|
forEach(f) {
|
|
let y;
|
|
while (!(y = this.next()).done) {
|
|
f(y.value);
|
|
}
|
|
/* extension: return final value from underlying iterator */
|
|
return y.value;
|
|
}
|
|
|
|
some(f) {
|
|
/* extension: if f is undefined, assume identity function */
|
|
if (typeof f === 'undefined') {
|
|
f = identity;
|
|
}
|
|
let y;
|
|
while (!(y = this.next()).done) {
|
|
if (f(y.value)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
every(f) {
|
|
/* extension: if f is undefined, assume identity function */
|
|
if (typeof f === 'undefined') {
|
|
f = identity;
|
|
}
|
|
let y;
|
|
while (!(y = this.next()).done) {
|
|
if (!f(y.value)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
find(f) {
|
|
/* extension: if f is undefined, return the first 'truthy' value */
|
|
if (typeof f === undefined) {
|
|
f = identity;
|
|
}
|
|
let y;
|
|
while (!(y = this.next()).done) {
|
|
if (f(y.value)) {
|
|
return y.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* extension */
|
|
includes(x) {
|
|
return this.some(function matches(y) { return y == x; });
|
|
}
|
|
|
|
/* extension */
|
|
strictlyIncludes(x) {
|
|
return this.some(function matches(y) { return y === x; });
|
|
}
|
|
};
|
|
|
|
export default Iterator;
|