import { at, concat, every, filter, find, first, type Flat, flat, flatMap, forEach, includes, isEmpty, join, last, map, type Mapping, next, type Predicate, prev, reduce, size, skip, some, sort, unique, } from './iterables.js'; import { mapValues } from './objects.js'; /** * {@label Iter} * Chain iterable operations, each acting on the output of the previous step * @example When the action is per item, the result is accessible as *iterable* * ``` * chain([0,1,2]) * .filter(i => i) * .map(i => i**2) * .iterable * // => [1,4] * ``` * @example When the action returns an element (as in first, next, reduce etc) the the result is accessible as *value* * ``` * chain([0,1,2]).filter(i => i).first().value * // => 1 * ``` * @example Iterable is always accessible, as a single element iterable * ``` * chain([0,1,2]).filter(i => i).first().iterable * // => [1] * ``` * @example Note if the action returned undefined, iterable will be empty * ``` * chain([]).first().iterable // => [] * chain([]).first().value // => undefined * ``` * @param value - initial iterable * @returns Chainable action on iterable */ export function chain(value: Iterable): IterableChain; /** * {@label Iter} * Chain iterable operations, each acting on the output of the previous step * @example When the action is per item, the result is accessible as *iterable* * ``` * chain([0,1,2]) * .filter(i => i) * .map(i => i**2) * .iterable * // => [1,4] * ``` * @example When the action returns an element (as in first, next, reduce etc) the the result is accessible as *value* * ``` * chain("hello").map(i => i.split("")).first().value * // => "h" * ``` * @example Iterable is always accessible, as a single element iterable * ``` * chain([0,1,2]).filter(i => i).first().iterable * // => [1] * ``` * @example Note if the action returned undefined, iterable will be empty * ``` * chain([]).first().iterable // => [] * chain([]).first().value // => undefined * ``` * @param value - initial value * @returns Chainable action on iterable */ export function chain>(value: V): ValueChain; export function chain(value: T) { const iterable = ( value === undefined ? [] : value === null ? [null] : typeof value === 'object' && Symbol.iterator in value ? value : [value] ) as Iter; return iterable === value ? chainIter(iterable) : chainElement(value); } function chainIter(iterable: Iterable): IterableChain { const toIter = { skip, map, flatMap, filter, concat, flat, unique, sort } as const; const toElm = { join, last, first, isEmpty, size, at, next, prev, find, some, includes, every, reduce } as const; const boundToIter = mapValues( toIter, (v) => (...args: unknown[]) => chainIter( (v as (iterable: Iterable, ...args: unknown[]) => Iterable)(iterable, ...args), ), ); const boundToElm = mapValues( toElm, (v) => (...args: unknown[]) => chainElement((v as (iterable: Iterable, ...args: unknown[]) => unknown)(iterable, ...args)), ); return { value: iterable, iterable, get array() { return [...iterable]; }, ...boundToIter, ...boundToElm, forEach: (mapping: Mapping) => { forEach(iterable, mapping); return chainIter(iterable); }, } as IterableChain; } function chainElement(value: T): ValueChain { const iterable = (value === undefined ? [] : [value]) as Iterable; return { ...chainIter(iterable), value, }; } export type IterableChain = Chain & { value: Iterable }; export type ValueChain = Chain & { value: T }; export type NotIterable = T extends Iterable ? never : T; export type Iter = T extends Iterable ? Iterable : Iterable; export type Chain = { last: () => ValueChain; first: () => ValueChain; isEmpty: () => ValueChain; size: () => ValueChain; at: (index: number) => ValueChain; next: () => ValueChain; prev: () => ValueChain; unique: () => IterableChain; map: (m: Mapping) => IterableChain; flatMap: (m: Mapping) => IterableChain>; filter: (p: Predicate) => IterableChain; concat: (...iterables: Iterable[]) => IterableChain; forEach: (fn: Mapping) => IterableChain; find: (p: Predicate) => ValueChain; includes: (element: T) => ValueChain; some: (p: Predicate) => ValueChain; sort: (p: Predicate) => IterableChain; every: (p: Predicate) => ValueChain; flat: () => IterableChain>; join: () => ValueChain; skip: (count: number) => IterableChain; reduce: (reducer: (acc: A, item: T) => A, initial: A) => ValueChain; iterable: Iterable; get array(): T[]; };