Skip to content

Instantly share code, notes, and snippets.

@jtmthf
Last active November 20, 2019 20:24
Show Gist options
  • Select an option

  • Save jtmthf/31a4fbdec5c6658f37e6518949fef5e7 to your computer and use it in GitHub Desktop.

Select an option

Save jtmthf/31a4fbdec5c6658f37e6518949fef5e7 to your computer and use it in GitHub Desktop.
export function all<T>(
iterable: Iterable<T>
): iterable is Iterable<Exclude<T, Falsy>>;
export function all<T, V extends T>(
iterable: Iterable<T>,
keyFn: TypePredicate<V, T>
): iterable is Iterable<V>;
export function all<T>(
iterable: Iterable<T>,
keyFn: Predicate<T> | keyof T
): boolean;
export function all<T>(
iterable: Iterable<T>,
keyFn: Predicate<T> | keyof T = x => !!x
): boolean {
for (const value of iterable) {
if (!(typeof keyFn === "function" ? keyFn(value) : value[keyFn])) {
return false;
}
}
return true;
}
export function allFP(): <T>(
iterable: Iterable<T>
) => iterable is Iterable<Exclude<T, Falsy>>;
export function allFP<T, V extends T>(
keyFn: TypePredicate<V, T>
): (iterable: Iterable<T>) => iterable is Iterable<V>;
export function allFP<T>(
keyFn: Predicate<T> | keyof T
): (iterable: Iterable<T>) => boolean;
export function allFP<T>(
keyFn?: Predicate<T> | keyof T
): (iterable: Iterable<T>) => boolean {
return iterable => all(iterable, keyFn!);
}
export function any<T>(
iterable: Iterable<T>,
keyFn: Predicate<T> | keyof T = x => !!x
): boolean {
for (const value of iterable) {
if (typeof keyFn === "function" ? keyFn(value) : value[keyFn]) {
return true;
}
}
return false;
}
export function anyFP<T = any>(
keyFN?: Predicate<T> | keyof T
): (iterable: Iterable<T>) => boolean {
return iterable => any(iterable, keyFN);
}
export function assertEqual<I, J extends I>(
a: Iterable<I>,
b: Iterable<J>
): asserts a is Iterable<J>;
export function assertEqual<I extends J, J>(
a: Iterable<I>,
b: Iterable<J>
): asserts b is Iterable<I>;
export function assertEqual<I, J>(a: Iterable<I>, b: Iterable<J>) {
const aIter = iter(a);
const bIter = iter(b);
for (;;) {
const { value: aValue, done: aDone } = aIter.next();
const { value: bValue, done: bDone } = bIter.next();
if (aValue !== bValue || aDone !== bDone) {
throw new Error();
}
if (aDone && bDone) {
return;
}
}
}
export function contains<T>(iterable: Iterable<T>, needle: T): boolean {
return any(iterable, value => value === needle);
}
export function containsFP<T>(needle: T): (iterable: Iterable<T>) => boolean {
return iterable => contains(iterable, needle);
}
export function* enumerate<T>(
iterable: Iterable<T>,
start = 0
): Iterable<[number, T]> {
for (const item of iterable) {
yield [start++, item];
}
}
export function enumerateFP(
start?: number
): <T>(iterable: Iterable<T>) => Iterable<[number, T]> {
return iterable => enumerate(iterable, start);
}
export function filter<T, V extends T>(
iterable: Iterable<T>,
predicate: TypePredicate<V, T>
): Iterable<V>;
export function filter<T>(
iterable: Iterable<T>,
predicate: Predicate<T>
): Iterable<T>;
export function* filter<T>(
iterable: Iterable<T>,
predicate: Predicate<T>
): Iterable<T> {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
export function filterFP<T, V extends T>(
predicate: TypePredicate<V, T>
): (iterable: Iterable<T>) => Iterable<V>;
export function filterFP<T>(
predicate: Predicate<T>
): (iterable: Iterable<T>) => Iterable<T>;
export function filterFP<T>(
predicate: Predicate<T>
): (iterable: Iterable<T>) => Iterable<T> {
return iterable => filter(iterable, predicate);
}
export function iter<T>(iterable: Iterable<T>): Iterator<T> {
return iterable[Symbol.iterator]();
}
export function* map<T, V>(
iterable: Iterable<T>,
mapper: (value: T) => V
): Iterable<V> {
for (const item of iterable) {
yield mapper(item);
}
}
export function mapFP<T, V>(
mapper: (value: T) => V
): (iterable: Iterable<T>) => Iterable<V> {
return iterable => map(iterable, mapper);
}
export function max<T>(
iterable: Iterable<T>,
keyFn: (value: T) => number
): Maybe<T>;
export function max<T>(iterable: Iterable<number>): Maybe<number>;
export function max<T>(
iterable: Iterable<T>,
keyFn?: (value: T) => number
): Maybe<T> {
let maxValue: Maybe<T>;
for (const value of iterable) {
if (maxValue === undefined || value > (keyFn ? keyFn(value) : value)) {
maxValue = value;
}
}
return maxValue;
}
export function maxFP<T = any>(
keyFn?: (value: T) => number
): (iterable: Iterable<T>) => Maybe<T> {
return iterable => max(iterable, keyFn!);
}
export function min<T>(
iterable: Iterable<T>,
keyFn: (value: T) => number
): Maybe<T>;
export function min<T>(iterable: Iterable<number>): Maybe<number>;
export function min<T>(
iterable: Iterable<T>,
keyFn?: (value: T) => number
): Maybe<T> {
let minValue: Maybe<T>;
for (const value of iterable) {
if (minValue === undefined || value < (keyFn ? keyFn(value) : value)) {
minValue = value;
}
}
return minValue;
}
export function minFP<T = any>(
keyFn?: (value: T) => number
): (iterable: Iterable<T>) => Maybe<T> {
return iterable => min(iterable, keyFn!);
}
export function range(stop: number): Iterable<number>;
export function range(
start: number,
stop: number,
step?: number
): Iterable<number>;
export function* range(
startOrStop: number,
stop?: number,
step = 1
): Iterable<number> {
let i = stop === undefined ? 0 : startOrStop;
stop = stop === undefined ? startOrStop : stop;
while (i < stop) {
yield i;
i += step;
}
}
export function reduce<T, O>(
iterable: Iterable<T>,
reducer: (previous: O, next: T) => O,
start: O
): O;
export function reduce<T>(
iterable: Iterable<T>,
reducer: (previous: T, next: T) => T
): Maybe<T>;
export function reduce<T, O>(
iterable: Iterable<T>,
reducer: (previous: O, next: T) => O,
start?: O
): O {
let startSupplied = arguments.length === 3;
let accumulator: any = start;
for (const value of iterable) {
if (!startSupplied) {
accumulator = value;
startSupplied = true;
continue;
}
accumulator = reducer(accumulator, value);
}
return accumulator;
}
export function reduceFP<T>(
reducer: (previous: T, next: T) => T
): {
(start: T): (iterable: Iterable<T>) => T;
(iterable: Iterable<T>): Maybe<T>;
};
export function reduceFP<T, O>(
reducer: (previous: O, next: T) => O
): (start: O) => (iterable: Iterable<T>) => O;
export function reduceFP<T, O>(
reducer: (previous: O, next: T) => O
): (startOrIterable: O | Iterable<T>) => any {
return startOrIterable => {
return isIterable(startOrIterable)
? reduce(startOrIterable, reducer as any)
: (iterable: Iterable<T>) => reduce(iterable, reducer, startOrIterable);
};
}
export function pipe<R>(func: () => R): Pipe<R, typeof func>;
export function pipe<A0, R>(func: (a0: A0) => R): Pipe<R, typeof func>;
export function pipe<A0, A1, R>(
func: (a0: A0, a1: A1) => R
): Pipe<R, typeof func>;
export function pipe<A0, A1, A2, R>(
func: (a0: A0, a1: A1, a2: A2) => R
): Pipe<R, typeof func>;
export function pipe<R>(func: (...args: any[]) => R): Pipe<R, typeof func> {
function composed(...args: any[]) {
return func(...args);
}
composed.pipe = <V>(pipedFunc: (value: R) => V) => {
return pipe((...args: any[]) => pipedFunc(func(...args)));
};
return composed;
}
export function isIterable(value: any): value is Iterable<any> {
return value && value[Symbol.iterator];
}
export type Falsy = false | 0 | 0n | "" | null | undefined;
export type Predicate<T> = (val: T) => boolean;
export type TypePredicate<T extends V, V = unknown> = (val: V) => val is T;
export type Maybe<T> = T | undefined;
type Pipe<T, F extends (...args: any[]) => any> = {
pipe<V>(func: (value: T) => V): Pipe<V, (...args: Parameters<F>) => V>;
} & F;
const sum = pipe(range)
.pipe(mapFP(x => x * x))
.pipe(filterFP(x => x % 2 === 0))
.pipe(reduceFP<number>((sum, next) => sum + next)(0))(1_000_000_000);
console.log(sum);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment