remeda
Version:
A utility library for JavaScript and Typescript.
116 lines (113 loc) • 4.91 kB
TypeScript
import { IsNumericLiteral, LessThan, IfNever, ValueOf, Subtract, IntRange } from 'type-fest';
import { I as IntRangeInclusive } from './IntRangeInclusive-Cn-qsrAN.js';
import { I as IterableContainer } from './IterableContainer-CtfinwiH.js';
import { N as NTuple } from './NTuple-BgsZT9dJ.js';
import { N as NonEmptyArray } from './NonEmptyArray-C9Od1wmF.js';
import { P as PartialArray } from './PartialArray-DqgYiDUP.js';
import { T as TupleParts } from './TupleParts-BeowYtF7.js';
import './RemedaTypeError-BIoNlKC-.js';
type MAX_LITERAL_SIZE = 350;
type Chunk<T extends IterableContainer, N extends number> = T extends readonly [] ? [] : IsNumericLiteral<N> extends true ? LessThan<N, 1> extends true ? never : LessThan<N, MAX_LITERAL_SIZE> extends true ? [
...LiteralChunk<T, N>
] : GenericChunk<T> : GenericChunk<T>;
type LiteralChunk<T extends IterableContainer, N extends number> = ChunkRestElement<ChunkFixedTuple<TuplePrefix<T>, N>, TupleParts<T>["item"], TupleParts<T>["suffix"], N> | ([...TuplePrefix<T>, ...TupleParts<T>["suffix"]] extends readonly [] ? [] : never);
/**
* This type **only** works if the input array `T` is a fixed tuple. For these
* inputs the chunked output could be computed as literal finite tuples too.
*/
type ChunkFixedTuple<T, N extends number, Result = []> = T extends readonly [infer Head, ...infer Rest] ? ChunkFixedTuple<Rest, N, Result extends [
...infer Previous extends Array<Array<unknown>>,
infer Current extends Array<unknown>
] ? Current["length"] extends N ? [
...Previous,
Current,
[Head]
] : [
...Previous,
[...Current, Head]
] : [
[Head]
]> : Result;
/**
* Here lies the main complexity of building the chunk type. It takes the prefix
* chunks, the rest param item type, and the suffix (not chunked!) and it
* creates all possible combinations of adding items to the prefix and suffix
* for all possible scenarios for how many items the rest param "represents".
*/
type ChunkRestElement<PrefixChunks, Item, Suffix extends Array<unknown>, N extends number> = IfNever<Item, PrefixChunks, PrefixChunks extends [
...infer PrefixFullChunks extends Array<Array<unknown>>,
infer LastPrefixChunk extends Array<unknown>
] ? ValueOf<{
[Padding in IntRangeInclusive<0, Subtract<N, LastPrefixChunk["length"]>>]: [
...PrefixFullChunks,
...ChunkFixedTuple<[
...LastPrefixChunk,
...NTuple<Item, Padding>,
...Suffix
], N>
];
}> | [
...PrefixFullChunks,
[
...LastPrefixChunk,
...NTuple<Item, Subtract<N, LastPrefixChunk["length"]>>
],
...Array<NTuple<Item, N>>,
...SuffixChunk<Suffix, Item, N>
] : [
...Array<NTuple<Item, N>>,
...SuffixChunk<Suffix, Item, N>
]>;
/**
* This type assumes it takes a finite tuple that represents the suffix of our
* input array. It builds all possible combinations of adding items to the
* **head** of the suffix in order to pad the suffix until the last chunk is
* full.
*/
type SuffixChunk<T extends Array<unknown>, Item, N extends number> = T extends readonly [] ? [
ValueOf<{
[K in IntRangeInclusive<1, N>]: NTuple<Item, K>;
}>
] : ValueOf<{
[Padding in IntRange<0, N>]: ChunkFixedTuple<[
...NTuple<Item, Padding>,
...T
], N>;
}>;
/**
* This is the legacy type used when we don't know what N is. We can only adjust
* our output based on if we know for sure that the array is empty or not.
*/
type GenericChunk<T extends IterableContainer> = T extends readonly [...Array<unknown>, unknown] | readonly [unknown, ...Array<unknown>] ? NonEmptyArray<NonEmptyArray<T[number]>> : Array<NonEmptyArray<T[number]>>;
type TuplePrefix<T extends IterableContainer> = [
...TupleParts<T>["required"],
...PartialArray<TupleParts<T>["optional"]>
];
/**
* Split an array into groups the length of `size`. If `array` can't be split evenly, the final chunk will be the remaining elements.
*
* @param array - The array.
* @param size - The length of the chunk.
* @signature
* R.chunk(array, size)
* @example
* R.chunk(['a', 'b', 'c', 'd'], 2) // => [['a', 'b'], ['c', 'd']]
* R.chunk(['a', 'b', 'c', 'd'], 3) // => [['a', 'b', 'c'], ['d']]
* @dataFirst
* @category Array
*/
declare function chunk<T extends IterableContainer, N extends number>(array: T, size: N): Chunk<T, N>;
/**
* Split an array into groups the length of `size`. If `array` can't be split evenly, the final chunk will be the remaining elements.
*
* @param size - The length of the chunk.
* @signature
* R.chunk(size)(array)
* @example
* R.chunk(2)(['a', 'b', 'c', 'd']) // => [['a', 'b'], ['c', 'd']]
* R.chunk(3)(['a', 'b', 'c', 'd']) // => [['a', 'b', 'c'], ['d']]
* @dataLast
* @category Array
*/
declare function chunk<N extends number>(size: N): <T extends IterableContainer>(array: T) => Chunk<T, N>;
export { chunk };