chunkByPredicate
Splits an array into chunks whenever the predicate returns true. The chunk boundary is placed before the element where the predicate succeeds. The predicate is applied to each element (excluding the first).
1/**
2 * Splits an array into chunks whenever the predicate returns true.
3 * The chunk boundary is placed before the element where the predicate succeeds.
4 * The predicate is applied to each element (excluding the first).
5 *
6 * @param arr - The array to split.
7 * @param predicate - A function that determines if a new chunk should start.
8 * @returns An array of chunked arrays.
9 */
10export function chunkByPredicate<T>(
11 arr: T[],
12 predicate: (current: T, index: number, array: T[]) => boolean
13): T[][] {
14 if (!arr.length) return [];
15
16 const result: T[][] = [];
17 let currentChunk: T[] = [arr[0]];
18
19 for (let i = 1; i < arr.length; i++) {
20 if (predicate(arr[i], i, arr)) {
21 result.push(currentChunk);
22 currentChunk = [arr[i]];
23 } else {
24 currentChunk.push(arr[i]);
25 }
26 }
27
28 result.push(currentChunk);
29 return result;
30}
Dynamic chunk boundaries
Unlike fixed-size chunking, this utility allows intelligent and flexible splitting based on contextual logic (e.g., value thresholds, type changes, time gaps).
Fine-grained control
The predicate receives
current
,index
, and the entirearray
, enabling advanced splitting strategies that can react to complex patterns in data.Efficient single pass
Processes the array in a single iteration, keeping the runtime efficient even for large datasets.
Non-destructive
Does not mutate the original array, ensuring safety when used in functional programming pipelines.
Robust against empty input
Gracefully handles empty arrays by returning an empty result without throwing errors.
Tests | Examples
1test('chunkByPredicate - chunks on even numbers', () => {
2 const input = [1, 3, 5, 2, 7, 2, 4];
3 const output = chunkByPredicate(input, x => x % 2 === 0);
4 expect(output).toEqual([[1, 3, 5], [2, 7], [2], [4]]);
5});
6
7test('chunkByPredicate - splits on negative numbers', () => {
8 const input = [10, 20, -1, 30, -2, 40];
9 const output = chunkByPredicate(input, x => x < 0);
10 expect(output).toEqual([[10, 20], [-1, 30], [-2, 40]]);
11});
12
13test('chunkByPredicate - empty array', () => {
14 expect(chunkByPredicate([], () => true)).toEqual([]);
15});
16
17test('chunkByPredicate - no predicate match', () => {
18 expect(chunkByPredicate([1, 2, 3], () => false)).toEqual([[1, 2, 3]]);
19});
20
21test('chunkByPredicate - predicate always true', () => {
22 expect(chunkByPredicate([1, 2, 3], () => true)).toEqual([[1], [2], [3]]);
23});
Common Use Cases
Log grouping or message segmentation
Useful for splitting logs or event sequences whenever a special entry or delimiter appears (e.g.,
chunkByPredicate(logs, l => l.level === 'ERROR')
).Data stream segmentation
For real-time systems, can group incoming packets/frames until a marker or reset flag is encountered.
Form input parsing
Helps split dynamic multi-line input or CSV-like data whenever a specific token appears (e.g., empty string or linebreak).
Sectioning UI data
Can split UI-rendered items (e.g., transactions, posts, measurements) into sections like "Today", "Yesterday", "Older", based on timestamp gaps.
Conditional batching
Enables chunking for threshold-based or grouped processing, e.g., new chunk starts every time a value exceeds 100.