Splitting an array into chunks might be a common problem you’ll run into in your TypeScript apps. In this article I’ll talk you through my approach, how that works, as well as the Lodash approach.
Using Reduce
One simple approach to this problem is to use reduce. Reduce is a very useful method for processing an array where you expect the output to be a completely different shape.
Dividing an array into batches of size n means that if you iterate through the array, every nth item should be the start of a new group.
Here’s my implementation of this approach:
function batchReduce<T>(arr: T[], batchSize: number): T[][] {
return arr.reduce((batches, curr, i) => {
if (i % batchSize === 0) batches.push([]);
batches[batches.length - 1].push(arr[i]);
return batches;
}, [] as T[][]);
}
I’ve created a generic function here, to tell TypeScript that the type of the output array is based on the input array.
The steps are:
- For each item in the array
- If the item’s index is divisible by the batch size, start a new batch
- Put the item into the last batch in the array
So for example with the array [a, b, c, d] and a batch size of two
- a’s index is 0, 0 mod 2 is 0, so we start a new group:
- batches = [ [ ] ]
- Then we push a to the last batch
- batches = [ [ a ] ]
- b’s index is 1, 1 mod 2 = 1, so we don’t create a new batch
- Then we push b to the last batch
- batches = [ [ a, b ] ]
- c’s index is 2, 2 mod 2 = 0
- batches = [ [ a, b ], [] ]
- Then we push c to the last batch
- batches = [ [ a, b ], [ c ] ]
- d’s index is 3, 3 mod 2 = 1, so no new batch
- batches = [ [ a, b ], [ c, d ] ]
This also has the handy benefit that we don’t run into any issues if the array does not fit into equal groups. The last group will just be smaller than the batch size.
Testing this function, we get exactly the output we’d expect:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(batch(array, 4));
//[ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9 ] ]
Lodash
Lodash is a very useful library containing a lot of utility functions. You may already be using this in your project, or you may simply prefer to rely on the support of an external library. In this case, Lodash has a very useful chunk method specifically for this scenario.
You can install Lodash, and the TypeScript types with:
npm i --save lodash
npm install --save @types/lodash
Once it’s installed, usage is exactly the same as my implementation, taking in your array to split, and the batch (or chunk) size:
import _ from 'lodash';
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(_.chunk(array, 4));
//[ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9 ] ]
Let’s take a look at the source code for Lodash’s implementation, to see how it works. I’ve annotated each line, to explain what’s happening.
function chunk(array, size = 1) {
size = Math.max(toInteger(size), 0) //If we try to pass in a negative chunk size, or anything that isn't a number, you get 0
const length = array == null ? 0 : array.length //Get the length of the array, but if the array is null, use 0
//If we don't actually have an array, or the chunk size is 0, return an empty array
if (!length || size < 1) {
return []
}
//Looping through the array
let index = 0 //Current index in the original array
let resIndex = 0 //Current index in the array of batches
const result = new Array(Math.ceil(length / size)) //The resulting array will be orignalLength/batchSize
while (index < length) { //While we haven't gone past the end of the array
//resIndex++ to move on to the next batch
//Then we're using lodash's slice function to get each batch
result[resIndex++] = slice(array, index, (index += size))
}
return result
}
The code looks very different, but it’s similar to our approach, with some extra error handling. It also uses another Lodash method, slice(), in order to get each batch.
Conclusion
Thanks for reading! Hopefully my code, or the Lodash solution has been able to cover your use case. If you’re still having any troubles, or if I’ve helped you out, feel free to leave a comment below!
๐ฌ Leave a comment