Last active
June 27, 2021 13:51
-
-
Save moatorres/69a0242067934f64e78e5e264b0eb6f2 to your computer and use it in GitHub Desktop.
Lazy-list in JS/Node (with ES6 classes)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { List } from './List' | |
// generator #1 | |
function* idMaker() { | |
let index = 1 | |
while (true && index < this.length) yield index++ | |
} | |
// generator #2 | |
function* fibonacci() { | |
let x = 1 | |
let y = 1 | |
yield* [0, x, y] | |
while (true) { | |
let next = x + y | |
yield next | |
x = y | |
y = next | |
} | |
} | |
let lista = new List(idMaker, 10) | |
let outra = new List(fibonacci, 10) | |
// console.log(lista) | |
console.log('toArray:', lista.toArray()) | |
console.log('head:', lista.head()) | |
console.log('headOrUndefined:', lista.headOrUndefined()) | |
console.log('headOrCompute:', lista.headOrCompute(() => 'bla')) | |
console.log('headOr:', lista.take(5).headOr('Huhuhu')) | |
console.log('sum:', lista.take(5).sum()) | |
console.log('toDictionary:', lista.take(5).toDictionary()) | |
console.log('ofType:', lista.take(5).ofType('number').toArray()) | |
console.log('instanceOf:', lista.take(5).instanceOf(List).toArray()) | |
console.log('some:', lista.take(5).some((a) => a > 3)) | |
console.log('some:', lista.take(5).some((a) => a > 6)) | |
console.log('all:', lista.take(5).all((a) => typeof a !== 'number')) | |
console.log('where:', lista.take(5).where((a) => a > 2).toArray()) | |
console.log('filter:',lista.take(5).filter((a) => a < 3 && a > 0).toArray()) | |
console.log('tail:', lista.take(5).tail().toArray()) | |
console.log('empty:', lista.take(5).empty()) | |
console.log('map:', lista.take(5).map((a) => a * 100).toArray()) | |
console.log('integers:', lista.integers().take(5).toArray()) | |
console.log('drop:', lista.take(5).drop(2).toArray()) | |
console.log('range:', lista.range(1, 10).toArray()) | |
console.log('from:', lista.from([{ nome: 'Moka' }, { nome: 'Floca' }]).toArray()) | |
console.log('concat:', lista.concat([50, 25]).toArray()) | |
console.log('toString:', lista.toString()) | |
console.log('inspect:', lista.inspect()) | |
console.log('reduce:', lista.reduce((acc, v) => acc + v * 2, 0)) | |
console.log('scan:', lista.take(3).scan((v) => v * 2, 2).toArray()) | |
console.log( | |
'toDictionary(key):', | |
lista | |
.from([ | |
{ id: '123abc', nome: 'Moka' }, | |
{ id: '123def', nome: 'Floca' }, | |
]) | |
.toDictionary('id') | |
) | |
console.log( | |
'of:', | |
lista | |
.of({ id: 1, nome: 'John' }, { id: 2, nome: 'Jane' }) | |
.map((v) => { | |
return { ...v, nome: v.nome + ' Doe' } | |
}) | |
.toArray() | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// a Haskell-like lazy-list | |
// adapted from this awesome gist: https://gist.github.com/gvergnaud/6e9de8e06ef65e65f18dbd05523c7ca9 | |
export class List { | |
constructor(generator, length) { | |
this[Symbol.iterator] = generator | |
this.length = length | |
} | |
// should be private | |
generator() { | |
return this[Symbol.iterator]() | |
} | |
// should be private | |
flattenArgs(args) { | |
return args.reduce((acc, curr) => { | |
return Array.isArray(curr) ? [...acc, ...curr] : [...acc, curr] | |
}, []) | |
} | |
static of(...args) { | |
return new List(function* () { | |
return yield* args | |
}, args.length) | |
} | |
static from(iterable) { | |
return iterable | |
? new List(function* () { | |
yield* iterable | |
}, iterable.length) | |
: this.empty() | |
} | |
static range(start, end, step = 1) { | |
return new List(function* () { | |
let i = start | |
while (i <= end) { | |
yield i | |
i += step | |
} | |
}, Math.floor((end - start + 1) / step)) | |
} | |
static empty() { | |
return new List(function* () {}, 0) | |
} | |
static get integers() { | |
return this.range(0, Infinity) | |
} | |
map(fn) { | |
const generator = this.generator() | |
return new List(function* () { | |
for (const value of generator) yield fn(value) | |
}, this.length) | |
} | |
reduce(reducer, seed) { | |
return this.toArray().reduce(reducer, seed) | |
} | |
ap(list) { | |
const generator = this[Symbol.iterator] | |
return new List(function* () { | |
for (const f of generator()) { | |
yield* list.map(f) | |
} | |
}, this.length) | |
} | |
// delete the first N elements from a list | |
drop(count) { | |
const generator = this.generator() | |
return new List(function* () { | |
let next = generator.next() | |
let n = 1 | |
while (!next.done) { | |
if (n > count) yield next.value | |
n++ | |
next = generator.next() | |
} | |
}, this.length - count) | |
} | |
// deletes the first element from a list | |
tail() { | |
return this.drop(1) | |
} | |
scan(fn, seed) { | |
const generator = this.generator() | |
return new List(function* () { | |
let acc = seed | |
for (const value of generator) yield (acc = fn(acc, value)) | |
}, this.length) | |
} | |
filter(fn) { | |
const generator = this.generator() | |
return new List(function* () { | |
for (const value of generator) if (fn(value)) yield value | |
}, this.length) | |
} | |
where(fn) { | |
return this.filter(fn) | |
} | |
concat(...args) { | |
const generator = this.generator() | |
const toAdd = this.flattenArgs(args) | |
return new List(function* () { | |
yield* generator | |
yield* toAdd | |
}, this.length + toAdd.length) | |
} | |
take(count) { | |
const generator = this.generator() | |
return new List( | |
function* () { | |
let next = generator.next() | |
let n = 0 | |
while (!next.done && count > n) { | |
yield next.value | |
n++ | |
next = generator.next() | |
} | |
}, | |
this.length > count ? count : this.length | |
) | |
} | |
all(fn) { | |
const generator = this.generator() | |
const newList = new List(function* () { | |
for (const value of generator) { | |
if (fn(value)) yield value | |
else return yield value | |
} | |
}, this.length) | |
return newList.toArray().length === this.length | |
} | |
any(fn) { | |
const generator = this.generator() | |
const newList = new List(function* () { | |
for (const value of generator) if (fn(value)) return yield value | |
}, this.length) | |
return newList.toArray().length >= 1 | |
} | |
some(fn) { | |
return this.any(fn) | |
} | |
ofType(type) { | |
return this.filter((a) => typeof a === type) | |
} | |
instanceOf(type) { | |
return this.filter((a) => a instanceof type) | |
} | |
toDictionary(key) { | |
return this.reduce((acc, curr, idx) => { | |
return key | |
? curr[key] | |
? { ...acc, [curr[key]]: curr } | |
: acc | |
: { ...acc, [idx]: curr } | |
}, {}) | |
} | |
zipWith (lazyList, zipper) { | |
const generator1 = this[Symbol.iterator] | |
const generator2 = lazyList[Symbol.iterator] | |
return new List(function* () { | |
const iterator1 = generator1() | |
const iterator2 = generator2() | |
let next1 = iterator1.next() | |
let next2 = iterator2.next() | |
let i = 0 | |
while (!next1.done && !next2.done) { | |
yield zipper(next1.value, next2.value) | |
next1 = iterator1.next() | |
next2 = iterator2.next() | |
} | |
}, this.length < lazyList.length ? this.length : lazyList.length) | |
} | |
sum() { | |
return this.toArray().reduce((acc, curr) => { | |
return typeof curr === 'number' ? acc + curr : 0 | |
}, 0) | |
} | |
head() { | |
return this[Symbol.iterator]().next().value | |
} | |
headOr(valueWhenUndefined) { | |
return this.headOrUndefined() || valueWhenUndefined | |
} | |
headOrUndefined() { | |
return this.generator().next().value | |
} | |
headOrCompute(fn) { | |
return this.headOrUndefined() || fn() | |
} | |
headOrThrow(msg) { | |
return ( | |
this.headOrUndefined() || | |
(() => { | |
throw new Error(msg) | |
})() | |
) | |
} | |
toArray() { | |
return [...this] | |
} | |
toIterable() { | |
return this.toArray() | |
} | |
toString() { | |
const displayedCount = 100 | |
return `List [ ${this.take(displayedCount).toArray().join(', ')}${ | |
this.length > displayedCount ? ' ...' : '' | |
} ]` | |
} | |
inspect() { | |
return this.toString() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment