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[];
};