Splitting an array in equal parts

I am looking for a Javascript Algorithm to split an array into chunks, but avoiding any small left overs. For example:

_.chunk([1, 2, 3, 4, 5, 6, 7], 3) // [[1, 2, 3], [4, 5, 6], [7]]

But I want this:

_.chunk([1, 2, 3, 4, 5, 6, 7], 3) // [[1, 2, 3], [4, 5], [6, 7]]

_.chunk([1, 2, 3, 4, 5, 6, 7], 4) // [[1, 2, 3, 4], [5, 6, 7]]

_.chunk([1, 2, 3, 4, 5, 6, 7], 5) // [[1, 2, 3, 4], [5, 6, 7]]

_.chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3) // [[1, 2, 3], [4, 5, 6], [7, 8], [9, 10]]

So basically the output is spread over several arrays with a maximum number of elements passed in as second argument.

19 thoughts on “Splitting an array in equal parts”

  1. You should recalculate the size, which might need to be smaller than given. Then calculate from where the size should be one less for the remaining chunks.

    So you will have potentially two different chunk sizes (which differ by 1). For both you can call the original _.chunk:

    function chunk(arr, size) {
        const count = Math.ceil(arr.length / size);
        size = Math.ceil(arr.length / count);
        const i = arr.length-(size-1)*(arr.length%size && size-(arr.length%size));
        return _.chunk(arr.slice(0, i), size).concat(
               _.chunk(arr.slice(i), size-1));
    }
    
    for (let i = 1; i < 9; i++) {
        console.log(i, JSON.stringify(chunk([1, 2, 3, 4, 5, 6, 7], i)));
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>
    Reply
  2. This is done without lodash, it generates an array of proper length then fills in the parts with slices of the input array. It’s only a few lines of code, depending how you count.

    The caveat is the slices are up to how the decimals round. For your last example, you want to chunk a 10-length array with a limit of 3 per chunk. Well, dividing it out, you’ll get the output:

    [ [1,2], [3,4,5], [6,7], [8,9,10] ]
    

    and not what you wanted:

    [ [1,2,3], [4,5,6], [7,8], [9,10] ]
    

    For most applications I don’t think it matters much. I’m using this to chunk large inputs to an API that is throttled.

    function chunk(array, limit) {
      const numChunks = Math.ceil(array.length / limit);
      return Array.from(
        { length: numChunks },
        (_, i) => array.slice(i * array.length / numChunks, (i + 1) * array.length / numChunks)
      );
    }
    
    console.log(chunk([1, 2, 3, 4, 5, 6, 7], 3));
    console.log(chunk([1, 2, 3, 4, 5, 6, 7], 4));
    console.log(chunk([1, 2, 3, 4, 5, 6, 7], 5));
    console.log(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3));

    An alternative solution uses recursion to chunk the "leftover" array. This one meets your criteria.

    function chunk(array, limit) {
      if (array.length <= limit) return [array];
      const perChunk = Math.ceil(array.length / Math.ceil(array.length / limit));
      return [array.slice(0, perChunk)].concat(chunk(array.slice(perChunk), limit));
    }
    
    console.log(chunk([1, 2, 3, 4, 5, 6, 7], 3));
    console.log(chunk([1, 2, 3, 4, 5, 6, 7], 4));
    console.log(chunk([1, 2, 3, 4, 5, 6, 7], 5));
    console.log(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3));
    Reply

Leave a Comment