You have a long list of items and you want to lay them out in rows of three, or send them to an API that only accepts batches of 50, or paginate them ten per page. Chunking is the building block: split one flat array into many fixed-size groups. Lodash ships this as _.chunk — you're going to implement it.
Write a chunk function that takes an array and a size, and returns a new array of sub-arrays. Each sub-array has size elements, except the last one which holds whatever is left over.
function chunk(array, size) {
// returns a new array of sub-arrays, each of length `size`
// (the last sub-array holds the remainder)
}
chunk([1, 2, 3, 4, 5], 2);
// → [[1, 2], [3, 4], [5]]
chunk(['a', 'b', 'c', 'd'], 4);
// → [['a', 'b', 'c', 'd']]
chunk([], 3) returns [].size <= 0, return []. Don't loop forever.You'll walk the input array in fixed-size steps, slicing out one group per step until you run out of elements.
You have a flat list of 100 search results and you want to lay them out 10 per row. Rather than writing row logic in the template, you'd hand it an array of 10 arrays. chunk is that conversion: give it the data and a size, get back batches you can iterate over.
Picture a ruler alongside the array. Mark off groups of size, left to right. Whatever runs past the final mark becomes the last group on its own. Nothing is dropped, nothing is duplicated.
A common first try is to loop one element at a time, opening a new group every size elements:
function chunkBroken(array, size) {
const out = [];
for (let i = 0; i < array.length; i++) {
if (i % size === 0) out.push([]);
out[out.length - 1].push(array[i]);
}
return out;
}
The happy path works, but the edge cases bite. chunkBroken([1, 2], 0) divides by zero — i % 0 is NaN, the condition is never true, no groups open, and every element is silently dropped. Negative size does the same. You also do one push per element where you could do one slice per group.
Step through the array in jumps of size, slicing one group per step. Guard size <= 0 up front so the loop is always finite.
function chunk(array, size) {
// Spec choice: non-positive size returns [] rather than throwing.
// Without this guard the loop below never advances and hangs.
if (size <= 0) return [];
const out = [];
for (let i = 0; i < array.length; i += size) {
// slice() copies — safe even if the caller later mutates `array`.
// slice clamps end to array.length, so the final group can be shorter.
out.push(array.slice(i, i + size));
}
return out;
}
module.exports = { chunk };
Two shifts from the naive version. The loop steps by size, so you do one iteration per group instead of one per element. And slice(i, i + size) handles the remainder: when i + size overshoots array.length, slice clamps to the end and the final group comes out short.
Take chunk([1, 2, 3, 4, 5], 2):
size is 2, not <= 0, so we keep going.array.slice(0, 2) returns [1, 2]. Push it. out is [[1, 2]].array.slice(2, 4) returns [3, 4]. Push it. out is [[1, 2], [3, 4]].array.slice(4, 6) returns [5]. slice clamped 6 to length, so one element comes out. out is [[1, 2], [3, 4], [5]].6 < 5 is false. Exit the loop and return out.size without guarding <= 0 — i % 0 is NaN and a for loop with a non-incrementing step is infinite. Return [] early.splice instead of slice — splice mutates the input. The spec says don't. Use slice.slice clamps the end index — you don't need a Math.min(i + size, array.length); slice already does that for you.currentGroup and mutating it next iteration shares the reference across groups. slice returns a fresh array each call and sidesteps the bug.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.
You have a long list of items and you want to lay them out in rows of three, or send them to an API that only accepts batches of 50, or paginate them ten per page. Chunking is the building block: split one flat array into many fixed-size groups. Lodash ships this as _.chunk — you're going to implement it.
Write a chunk function that takes an array and a size, and returns a new array of sub-arrays. Each sub-array has size elements, except the last one which holds whatever is left over.
function chunk(array, size) {
// returns a new array of sub-arrays, each of length `size`
// (the last sub-array holds the remainder)
}
chunk([1, 2, 3, 4, 5], 2);
// → [[1, 2], [3, 4], [5]]
chunk(['a', 'b', 'c', 'd'], 4);
// → [['a', 'b', 'c', 'd']]
chunk([], 3) returns [].size <= 0, return []. Don't loop forever.You'll walk the input array in fixed-size steps, slicing out one group per step until you run out of elements.
You have a flat list of 100 search results and you want to lay them out 10 per row. Rather than writing row logic in the template, you'd hand it an array of 10 arrays. chunk is that conversion: give it the data and a size, get back batches you can iterate over.
Picture a ruler alongside the array. Mark off groups of size, left to right. Whatever runs past the final mark becomes the last group on its own. Nothing is dropped, nothing is duplicated.
A common first try is to loop one element at a time, opening a new group every size elements:
function chunkBroken(array, size) {
const out = [];
for (let i = 0; i < array.length; i++) {
if (i % size === 0) out.push([]);
out[out.length - 1].push(array[i]);
}
return out;
}
The happy path works, but the edge cases bite. chunkBroken([1, 2], 0) divides by zero — i % 0 is NaN, the condition is never true, no groups open, and every element is silently dropped. Negative size does the same. You also do one push per element where you could do one slice per group.
Step through the array in jumps of size, slicing one group per step. Guard size <= 0 up front so the loop is always finite.
function chunk(array, size) {
// Spec choice: non-positive size returns [] rather than throwing.
// Without this guard the loop below never advances and hangs.
if (size <= 0) return [];
const out = [];
for (let i = 0; i < array.length; i += size) {
// slice() copies — safe even if the caller later mutates `array`.
// slice clamps end to array.length, so the final group can be shorter.
out.push(array.slice(i, i + size));
}
return out;
}
module.exports = { chunk };
Two shifts from the naive version. The loop steps by size, so you do one iteration per group instead of one per element. And slice(i, i + size) handles the remainder: when i + size overshoots array.length, slice clamps to the end and the final group comes out short.
Take chunk([1, 2, 3, 4, 5], 2):
size is 2, not <= 0, so we keep going.array.slice(0, 2) returns [1, 2]. Push it. out is [[1, 2]].array.slice(2, 4) returns [3, 4]. Push it. out is [[1, 2], [3, 4]].array.slice(4, 6) returns [5]. slice clamped 6 to length, so one element comes out. out is [[1, 2], [3, 4], [5]].6 < 5 is false. Exit the loop and return out.size without guarding <= 0 — i % 0 is NaN and a for loop with a non-incrementing step is infinite. Return [] early.splice instead of slice — splice mutates the input. The spec says don't. Use slice.slice clamps the end index — you don't need a Math.min(i + size, array.length); slice already does that for you.currentGroup and mutating it next iteration shares the reference across groups. slice returns a fresh array each call and sidesteps the bug.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.