UNPKG

ts-prime

Version:

A utility library for JavaScript and Typescript.

196 lines (183 loc) 4.27 kB
import { LazyResult } from './_reduceLazy'; /** * Perform left-to-right function composition. * @param value The initial value. * @param operations the list of operations to apply. * @signature P.pipe(data, op1, op2, op3) * @example * P.pipe( * [1, 2, 3, 4], * P.map(x => x * 2), * arr => [arr[0] + arr[1], arr[2] + arr[3]], * ) // => [6, 14] * * * @data_first * @category Function */ export function pipe<A, B>(value: A, op1: (input: A) => B): B; export function pipe<A, B, C>( value: A, op1: (input: A) => B, op2: (input: B) => C ): C; export function pipe<A, B, C, D>( value: A, op1: (input: A) => B, op2: (input: B) => C, op3: (input: C) => D ): D; export function pipe<A, B, C, D, E>( value: A, op1: (input: A) => B, op2: (input: B) => C, op3: (input: C) => D, op4: (input: D) => E ): E; export function pipe<A, B, C, D, E, F>( value: A, op1: (input: A) => B, op2: (input: B) => C, op3: (input: C) => D, op4: (input: D) => E, op5: (input: E) => F ): F; export function pipe<A, B, C, D, E, F, G>( value: A, op1: (input: A) => B, op2: (input: B) => C, op3: (input: C) => D, op4: (input: D) => E, op5: (input: E) => F, op6: (input: F) => G ): G; export function pipe<A, B, C, D, E, F, G, H>( value: A, op1: (input: A) => B, op2: (input: B) => C, op3: (input: C) => D, op4: (input: D) => E, op5: (input: E) => F, op6: (input: F) => G, op7: (input: G) => H ): H; export function pipe( value: any, ...operations: Array<(value: any) => any> ): any { let ret = value; const lazyOps = operations.map(op => { const { lazy, lazyArgs } = op as LazyOp; if (lazy) { const fn: any = lazy(...lazyArgs); fn.indexed = lazy.indexed; fn.single = lazy.single; fn.index = 0; fn.items = []; return fn; } return null; }); let opIdx = 0; while (opIdx < operations.length) { const op = operations[opIdx]; const lazyOp = lazyOps[opIdx]; if (!lazyOp) { ret = op(ret); opIdx++; continue; } const lazySeq: LazyFn[] = []; for (let j = opIdx; j < operations.length; j++) { if (lazyOps[j]) { lazySeq.push(lazyOps[j]); if (lazyOps[j].single) { break; } } else { break; } } let acc: any[] = []; for (let j = 0; j < ret.length; j++) { let item = ret[j]; if (_processItem({ item, acc, lazySeq })) { break; } } const lastLazySeq = lazySeq[lazySeq.length - 1]; if ((lastLazySeq as any).single) { ret = acc[0]; } else { ret = acc; } opIdx += lazySeq.length; } return ret; } type LazyFn = (value: any, index?: number, items?: any) => LazyResult<any>; type LazyOp = ((input: any) => any) & { lazy: ((...args: any[]) => LazyFn) & { indexed: boolean; single: boolean; }; lazyArgs: any[]; }; function _processItem({ item, lazySeq, acc, }: { item: any; lazySeq: any[]; acc: any[]; }): boolean { if (lazySeq.length === 0) { acc.push(item); return false; } let lazyResult: LazyResult<any> = { done: false, hasNext: false }; let isDone = false; for (let i = 0; i < lazySeq.length; i++) { const lazyFn = lazySeq[i]; const indexed = lazyFn.indexed; const index = lazyFn.index; const items = lazyFn.items; items.push(item); lazyResult = indexed ? lazyFn(item, index, items) : lazyFn(item); lazyFn.index++; if (lazyResult.hasNext) { if (lazyResult.hasMany) { const nextValues: any[] = lazyResult.next; for (const subItem of nextValues) { const subResult = _processItem({ item: subItem, acc, lazySeq: lazySeq.slice(i + 1), }); if (subResult) { return true; } } return false; } else { item = lazyResult.next; } } if (!lazyResult.hasNext) { break; } // process remaining functions in the pipe // but don't process remaining elements in the input array if (lazyResult.done) { isDone = true; } } if (lazyResult.hasNext) { acc.push(item); } if (isDone) { return true; } return false; }