UNPKG

pragmatic-fp-ts

Version:

Opinionated functional programming library with easy use in mind

158 lines (136 loc) 4.84 kB
import { butLast, flow, isArray, isFunction, isLeft, isNothing, last, reduce } from "./main.ts"; import { Mappable, Predicate } from "./types.ts"; // Mutating conj! Expects empty new array input export const conjM = <A>(coll: A[], elem: A) => { coll.push(elem); return coll; }; // Mutating cons! Expects empty new array input export const consM = <A>(coll: A[], elem: A) => { coll.unshift(elem); return coll; }; type Transform<TColl, TNext> = (coll: TColl, next: TNext) => TColl; type Reducer<TColl, TNext> = (coll: TColl, next: TNext) => TColl; type Transducer<TColl, TNext> = (reducing: Reducer<TColl, TNext>) => Transform<TColl, TNext>; type MapTransducer<TColl, TInput, TNext> = ( reducing: Reducer<TColl, TNext> ) => Transform<TColl, TInput>; export const map = <A, B, TColl>(fn: Mappable<A, B>): MapTransducer<TColl, A, B> => ( reducing: Reducer<TColl, B> ) => (coll: TColl, input: A) => reducing(coll, fn(input)); export const flatMap = <A, B>(fn: Mappable<A, B | B[]>) => (reducing: Reducer<any, any>) => ( coll: any, input: any ) => { const mapped = fn(input); return isArray(mapped) ? reduce(reducing, coll, mapped) : reducing(coll, mapped); }; export const flatten = <TColl, TNext>(reducing: Reducer<TColl, TNext>) => ( coll: TColl, input: TNext | TNext[] ) => (isArray(input) ? reduce(reducing, coll, input) : reducing(coll, input)); export const filter = <TInput, TColl>(pred: Predicate<TInput>): Transducer<TColl, TInput> => ( reducing: Reducer<TColl, TInput> ) => (coll: TColl, input: TInput) => (pred(input) ? reducing(coll, input) : coll); export const take = <TNext, TColl>(n: number) => (reducing: Reducer<TColl, TNext>) => { let i = 0; return (coll: TColl, input: TNext) => { return i++ < n ? reducing(coll, input) : coll; }; }; export const drop = (n: number) => <TInput, TColl>(reducing: Reducer<TColl, TInput>) => { let i = 0; return (coll: TColl, input: TInput) => { return i++ < n ? coll : reducing(coll, input); }; }; export const insert = <TInput>(elem: TInput) => <TColl>(reducing: Reducer<TColl, TInput>) => { let inserted = false; return (coll: TColl, input: TInput) => { if (inserted) return reducing(coll, input); else { inserted = true; return reducing(reducing(coll, elem), input); } }; }; export const uniq = <TColl, TNext>(reducing: Reducer<TColl, TNext>) => { const set = new Set<TNext>(); return (coll: TColl, input: TNext) => { if (set.has(input)) return coll; else { set.add(input); return reducing(coll, input); } }; }; export const uniqBy = <TNext>(toKey: (val: TNext) => any) => <TColl>( reducing: Reducer<TColl, TNext> ) => { const set = new Set(); return (coll: TColl, input: TNext) => { const key = toKey(input); if (set.has(key)) return coll; else { set.add(key); return reducing(coll, input); } }; }; export const find = <TInput>(pred: Predicate<TInput>) => <TColl>( reducing: Reducer<TColl, TInput> ) => { let found = false; return (coll: TColl, input: TInput) => { if (!found && pred(input)) return reducing(coll, input); else return coll; }; }; export const compact = filter( (x: unknown) => x !== null && x !== undefined && (typeof x === "number" ? !isNaN(x as any) : true) && !isNothing(x) && !isLeft(x) ); export const insertM = <T>(inOrder: (a: T, b: T) => boolean, arr: T[], elem: T) => { let l = 0; let r = arr.length; while (r > l) { const m = (l + r) >> 1; // Math.floor(r + l / 2); if (inOrder(elem, arr[m])) r = m; else l = m + 1; } arr.splice(l, 0, elem); return arr; }; export function transformList<A = any, B = any>(...args: [...Transducer<any, any>[], A[]]): B[]; export function transformList<A = any, B = any>( ...args: Transducer<any, any>[] ): (coll: A[]) => B[]; export function transformList<A, B>(...args: any[]) { if (!Array.isArray(args[args.length - 1])) return (coll_: A[]) => transformList(...(args as any), coll_ as any); const xforms = butLast(args); const coll = last(args); const xform = flow(...(xforms.reverse() as any)); return reduce(xform(conjM), [], coll) as B[]; } export const intoObjM = (coll: any, [k, v]: [string | symbol, any]) => { coll[k] = v; return coll; }; export function transformObj<A = any, B = any>( ...transducers: Transducer<any, any>[] ): (obj: A) => B; export function transformObj<A = any, B = any>(...args: [...Transducer<any, any>[], A]): B; export function transformObj<A, B>(...args: any[]) { if (isFunction(args[args.length - 1])) return (coll_: A) => transformObj(...(args as any), coll_ as any); const xforms = butLast(args); const coll = last(args); const xform = flow(...(xforms.reverse() as any)); return reduce(xform(intoObjM), {}, Object.entries(coll)) as B; }