Skip to content

Instantly share code, notes, and snippets.

@ConorOBrien-Foxx
Created March 19, 2019 19:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ConorOBrien-Foxx/223ce3600daa11e30efb69254add3b04 to your computer and use it in GitHub Desktop.
Save ConorOBrien-Foxx/223ce3600daa11e30efb69254add3b04 to your computer and use it in GitHub Desktop.
Sane enumerators for JavaScript using Generators
// for compatibility with bignumbers/regular numbers
// getZero(5n) = 0n
// getZero(5) = 0
let getZero = function (entity) {
return entity - entity;
}
let getOne = function (entity) {
let zero = getZero(entity);
zero++;
return zero;
}
class Enumerator {
constructor(gen) {
this.generator = gen;
}
static range(...args) {
return new Enumerator.Range(...args);
}
forEach(fn) {
let onceGen = this.generator();
for(let el of onceGen) {
fn(el);
}
}
get length() {
return toArray().length;
}
empty() {
let onceGen = this.generator();
return onceGen.next().done;
}
map(fn) {
return new Enumerator(function* () {
let onceGen = this.generator();
for(let el of onceGen) {
yield fn(el);
}
}.bind(this));
}
withIndex(start = 0) {
return new Enumerator(function* () {
let onceGen = this.generator();
let i = start;
for(let el of onceGen) {
yield [el, i];
i++;
}
}.bind(this));
}
takeWhile(fn) {
return new Enumerator(function* () {
let onceGen = this.generator();
for(let el of onceGen) {
if(!fn(el)) {
break;
}
yield el;
}
}.bind(this));
}
dropWhile(fn) {
return new Enumerator(function* () {
let onceGen = this.generator();
let started = false;
let el, val;
do {
el = onceGen.next();
val = el.value;
if(started) {
yield val;
}
else {
started = !fn(val);
}
} while(!el.done);
}.bind(this));
}
toArray() {
let res = [];
this.forEach(el => res.push(el));
return res;
}
get(index) {
let onceGen = this.generator();
let el;
do {
el = onceGen.next();
} while(index --> 0);
return el.value;
}
first(n = Enumerator.NO_PARAMETER) {
if(n == Enumerator.NO_PARAMETER) {
return this.get(0);
}
else {
return this.withIndex()
.takeWhile(([e, i]) => i < n)
.map(([e, i]) => e);
}
}
reduce(fn, seed = Enumerator.NO_PARAMETER) {
let onceGen = this.generator();
let acc = onceGen.next();
if(acc.done) {
return seed === Enumerator.NO_PARAMETER ? undefined : seed;
}
acc = acc.value;
let cur = onceGen.next();
while(!cur.done) {
acc = fn(acc, cur.value);
cur = onceGen.next();
}
return acc;
}
sum(fn = x => x, zero = 0) {
if(this.empty()) {
return zero;
}
return this.map(fn).reduce((a, b) => a + b);
}
toString() {
return "[" + this.toArray().join(", ") + "]";
}
}
Enumerator.NO_PARAMETER = Symbol("Enumerator.NO_PARAMETER");
Enumerator.Range = class extends Enumerator {
constructor(lower, upper = Enumerator.NO_PARAMETER, step = Enumerator.NO_PARAMETER) {
if(upper === Enumerator.NO_PARAMETER) {
upper = lower;
lower = getZero(upper);
}
if(step === Enumerator.NO_PARAMETER) {
step = getOne(upper);
}
super(function* () {
for(let i = lower; i < upper; i += step) {
yield i;
}
});
this.lower = lower;
this.upper = upper;
}
get(index) {
let val = this.lower + index * step;
return val < this.upper ? val : undefined;
}
}
Enumerator.Nats = Enumerator.range(1n, Infinity);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment