Skip to content

Instantly share code, notes, and snippets.

@mattmess1221
Created March 20, 2024 20:56
Show Gist options
  • Save mattmess1221/35fd8ad3e9931afcd54319a15116131d to your computer and use it in GitHub Desktop.
Save mattmess1221/35fd8ad3e9931afcd54319a15116131d to your computer and use it in GitHub Desktop.
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