Last active
July 30, 2020 16:39
-
-
Save MichaelBelousov/cc148a7debc9aa5be3ca8c5daa03ad92 to your computer and use it in GitHub Desktop.
glorious lazy iterables in typescript/javascript
This file contains 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
/** return whether arg is T or an iterable of T | |
* for the flat function */ | |
function isIterable<T>(arg: T | Iterable<T>): arg is Iterable<T> { | |
return typeof arg === "object" && Symbol.iterator in arg; | |
} | |
/** iterable wrapper for functional programming with lazy composition */ | |
export default class Lazy<T> implements Iterator<T> { | |
static from<T>(iterable: Iterable<T>) { | |
return new Lazy<T>(iterable[Symbol.iterator]()) | |
} | |
public constructor(protected iterator: Iterator<T>) {} | |
public next(): ReturnType<Iterator<T>["next"]> { | |
return this.iterator.next() | |
} | |
[Symbol.iterator](): Iterator<T> { | |
return this | |
} | |
public filter(predicate: (t: T) => boolean) { | |
const _this = this | |
return Lazy.from({ | |
*[Symbol.iterator]() { | |
for (const t of _this) | |
if (predicate(t)) | |
yield t | |
} | |
}) | |
} | |
public map<U>(transform: (t: T) => U) { | |
const _this = this | |
return Lazy.from<U>({ | |
*[Symbol.iterator]() { | |
for (const t of _this) | |
yield transform(t) | |
} | |
}) | |
} | |
public flat(depth = 1) { | |
const _this = this; | |
if (depth <= 0) return this; | |
else return Lazy.from({ | |
*[Symbol.iterator]() { | |
for (const item of _this) { | |
if (isIterable(item)) | |
yield* Lazy.from(item).flat(depth - 1) | |
else yield item | |
} | |
} | |
}) | |
} | |
public concat(...args: (Iterable<T> | T)[] ): Lazy<T> { | |
const _this = this | |
return Lazy.from({ | |
*[Symbol.iterator]() { | |
yield* _this; | |
for (const arg of args) | |
if (isIterable(arg)) yield* arg; | |
else yield arg; | |
} | |
}) | |
} | |
public forEach(doSomething: (t: T) => void) { | |
for (const item of this) | |
doSomething(item) | |
} | |
public take(n: number): Lazy<T> { | |
const _this = this; | |
return Lazy.from({ | |
*[Symbol.iterator]() { | |
let i = 0; | |
for (const item of _this) { | |
if (!(i < n)) break | |
yield item | |
i++ | |
} | |
} | |
}) | |
} | |
public reduce<Result>(callback: (prev: Result, curr: T, index: number) => Result, initial: Result): Result { | |
let result = initial; | |
let i = 0; | |
for (const curr of this) { | |
result = callback(result, curr, i); | |
i++; | |
} | |
return result; | |
} | |
public toSet(): Set<T> { | |
const result = new Set<T>() | |
for (const item of this) | |
result.add(item) | |
return result | |
} | |
public some(predicate: (t: T) => boolean): boolean { | |
for (const item of this) | |
if (predicate(item)) return true | |
return false | |
} | |
public every(predicate: (t: T) => boolean): boolean { | |
return !this.some(t => !predicate(t)) | |
} | |
public empty(): boolean { | |
const item = this.next() | |
return item.done | |
} | |
public sort(...[sortFunc]: Parameters<Array<T>["sort"]>) { | |
return Lazy.from([...this].sort(sortFunc)) | |
} | |
public get length() { | |
let i = 0 | |
for (const item of this) i++ | |
return i | |
} | |
public includes(t: T) { | |
for (const item of this) if (item === t) return true; | |
return false; | |
} | |
public find(predicate: (t: T) => boolean) { | |
for (const item of this) if (predicate(item)) return item; | |
return false; | |
} | |
} | |
/** | |
Allows stuff like following: | |
[...Lazy.from([1,2,3,[4,[5]]]) | |
.concat([1,2,3]) | |
.flat(2) | |
.filter(x => x%2==0) | |
.map(x => x * 3) | |
] | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment