Created
March 20, 2024 20:56
-
-
Save mattmess1221/35fd8ad3e9931afcd54319a15116131d to your computer and use it in GitHub Desktop.
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
type Flow<T, O> = Iterable<T> & { | |
[K in keyof O]: O[K] extends Function ? O[K] : never; | |
} & { | |
flow<U>(iterable: (this: Flow<T, O>) => Iterable<U>): Flow<U, O>; | |
}; | |
type FlowFactory<O> = { | |
<T>(iterable: Iterable<T>): Flow<T, O>; | |
}; | |
export function create<O extends object>(opts: O): FlowFactory<O> { | |
function flowMember<T, U>( | |
this: Flow<T, O>, | |
iterable: (this: Flow<T, O>) => Iterable<T> | |
): Flow<U, O> { | |
return flow({ | |
[Symbol.iterator]: () => iterable.bind(this)(), | |
}); | |
} | |
const flow = <T>(iterable: Iterable<T>) => { | |
return new Proxy(iterable, { | |
get(target, p, receiver) { | |
if ("flow" === p) { | |
return flowMember.bind(receiver); | |
} | |
if (p in opts) { | |
return Reflect.get(opts, p, receiver); | |
} | |
return Reflect.get(target, p, receiver); | |
}, | |
}) as Flow<T, O>; | |
}; | |
return flow; | |
} | |
type Operator<T, U> = (value: T) => U; | |
type Predicate<T> = (value: T) => any; | |
export function map<T, U, O>( | |
this: Flow<T, O>, | |
mapper: Operator<T, U> | |
): Flow<U, O> { | |
return this.flow(function* () { | |
for (const item of this) { | |
yield mapper(item); | |
} | |
}); | |
} | |
export function flatMap<T, U, O>( | |
this: Flow<T, O>, | |
mapper: Operator<T, Iterable<U>> | |
): Flow<U, O> { | |
return this.flow(function* () { | |
for (const item of this) { | |
for (const subitem of mapper(item)) { | |
yield subitem; | |
} | |
} | |
}); | |
} | |
export function filter<T, O>( | |
this: Flow<T, O>, | |
predicate: Predicate<T> | |
): Flow<T, O> { | |
return this.flow(function* () { | |
for (const item of this) { | |
if (predicate(item)) { | |
yield item; | |
} | |
} | |
}); | |
} | |
export function peek<T, O>( | |
this: Flow<T, O>, | |
callback: (item: T) => void | |
): Flow<T, O> { | |
return this.flow(function* () { | |
for (const item of this) { | |
callback(item); | |
yield item; | |
} | |
}); | |
} | |
export function unique<T, O>( | |
this: Flow<T, O>, | |
by?: (item: T) => any | |
): Flow<T, O> { | |
if (by === undefined) { | |
by = (item) => item; | |
} | |
return this.flow(function* () { | |
const set = new Set(); | |
for (const item of this) { | |
if (!set.has(item)) { | |
set.add(item); | |
yield item; | |
} | |
} | |
}); | |
} | |
export function skip<T, O>(this: Flow<T, O>, skip: number) { | |
return this.flow(function* () { | |
let count = 0; | |
for (const item of this) { | |
if (count < skip) { | |
count += 1; | |
continue; | |
} | |
yield item; | |
} | |
}); | |
} | |
export function limit<T, O>(this: Flow<T, O>, limit: number) { | |
return this.flow(function* () { | |
let count = 0; | |
for (const item of this) { | |
if (count > limit) { | |
break; | |
} | |
count += 1; | |
yield item; | |
} | |
}); | |
} | |
export function forEach<T>( | |
this: Flow<T, unknown>, | |
callback: (value: T) => void | |
): void { | |
for (const item of this) { | |
callback(item); | |
} | |
} | |
export function toArray<T, O>(this: Flow<T, O>): T[] { | |
return [...this]; | |
} | |
const flow = create({ | |
map, | |
flatMap, | |
filter, | |
toArray, | |
forEach, | |
}); | |
const planets = [ | |
"mercury", | |
"venux", | |
"earth", | |
"mars", | |
"jupiter", | |
"saturn", | |
"uranus", | |
"neptune", | |
]; | |
flow(planets) | |
.filter((s) => s.length > 5) | |
.map((s) => `Planet ${s}`) | |
.forEach((item) => { | |
console.log(item); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment