Split array into chunks

Let's say that I have an Javascript array looking as following:

["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.

What approach would be appropriate to chunk (split) the array into many smaller arrays with, lets say, 10 elements at its most?

Answers:

Answer

The array.slice method can extract a slice from the beginning, middle, or end of an array for whatever purposes you require, without changing the original array.

var i,j,temparray,chunk = 10;
for (i=0,j=array.length; i<j; i+=chunk) {
    temparray = array.slice(i,i+chunk);
    // do whatever
}
Answer

Modified from an answer by dbaseman: https://stackoverflow.com/a/10456344/711085

Object.defineProperty(Array.prototype, 'chunk_inefficient', {
  value: function(chunkSize) {
    var array = this;
    return [].concat.apply([],
      array.map(function(elem, i) {
        return i % chunkSize ? [] : [array.slice(i, i + chunkSize)];
      })
    );
  }
});

console.log(
  [1, 2, 3, 4, 5, 6, 7].chunk_inefficient(3)
)
// [[1, 2, 3], [4, 5, 6], [7]]


minor addendum:

I should point out that the above is a not-that-elegant (in my mind) workaround to use Array.map. It basically does the following, where ~ is concatenation:

[[1,2,3]]~[]~[]~[] ~ [[4,5,6]]~[]~[]~[] ~ [[7]]

It has the same asymptotic running time as the method below, but perhaps a worse constant factor due to building empty lists. One could rewrite this as follows (mostly the same as Blazemonger's method, which is why I did not originally submit this answer):

More efficient method:

// refresh page if experimenting and you already defined Array.prototype.chunk

Object.defineProperty(Array.prototype, 'chunk', {
  value: function(chunkSize) {
    var R = [];
    for (var i = 0; i < this.length; i += chunkSize)
      R.push(this.slice(i, i + chunkSize));
    return R;
  }
});

console.log(
  [1, 2, 3, 4, 5, 6, 7].chunk(3)
)


My preferred way nowadays is the above, or one of the following:

Array.range = function(n) {
  // Array.range(5) --> [0,1,2,3,4]
  return Array.apply(null,Array(n)).map((x,i) => i)
};

Object.defineProperty(Array.prototype, 'chunk', {
  value: function(n) {

    // ACTUAL CODE FOR CHUNKING ARRAY:
    return Array.range(Math.ceil(this.length/n)).map((x,i) => this.slice(i*n,i*n+n));

  }
});

Demo:

> JSON.stringify( Array.range(10).chunk(3) );
[[1,2,3],[4,5,6],[7,8,9],[10]]

Or if you don't want an Array.range function, it's actually just a one-liner (excluding the fluff):

var ceil = Math.ceil;

Object.defineProperty(Array.prototype, 'chunk', {value: function(n) {
    return Array(ceil(this.length/n)).fill().map((_,i) => this.slice(i*n,i*n+n));
}});

or

Object.defineProperty(Array.prototype, 'chunk', {value: function(n) {
    return Array.from(Array(ceil(this.length/n)), (_,i)=>this.slice(i*n,i*n+n));
}});
Answer

Try to avoid mucking with native prototypes, including Array.prototype, if you don't know who will be consuming your code (3rd parties, coworkers, yourself at a later date, etc.).

There are ways to safely extend prototypes (but not in all browsers) and there are ways to safely consume objects created from extended prototypes, but a better rule of thumb is to follow the Principle of Least Surprise and avoid these practices altogether.

If you have some time, watch Andrew Dupont's JSConf 2011 talk, "Everything is Permitted: Extending Built-ins", for a good discussion about this topic.

But back to the question, while the solutions above will work, they are overly complex and requiring unnecessary computational overhead. Here is my solution:

function chunk (arr, len) {

  var chunks = [],
      i = 0,
      n = arr.length;

  while (i < n) {
    chunks.push(arr.slice(i, i += len));
  }

  return chunks;
}

// Optionally, you can do the following to avoid cluttering the global namespace:
Array.chunk = chunk;
Answer

Here's a ES6 version using reduce

var perChunk = 2 // items per chunk    

var inputArray = ['a','b','c','d','e']

var result = inputArray.reduce((resultArray, item, index) => { 
  const chunkIndex = Math.floor(index/perChunk)

  if(!resultArray[chunkIndex]) {
    resultArray[chunkIndex] = [] // start a new chunk
  }

  resultArray[chunkIndex].push(item)

  return resultArray
}, [])

console.log(result); // result: [['a','b'], ['c','d'], ['e']]

And you're ready to chain further map/reduce transformations. Your input array is left intact


If you prefer a shorter but less readable version, you can sprinkle some concat into the mix for the same end result:

inputArray.reduce((all,one,i) => {
   const ch = Math.floor(i/perChunk); 
   all[ch] = [].concat((all[ch]||[]),one); 
   return all
}, [])
Answer

I tested the different answers into jsperf.com. The result is available there: https://web.archive.org/web/20150909134228/https://jsperf.com/chunk-mtds

And the fastest function (and that works from IE8) is this one:

function chunk(arr, chunkSize) {
  var R = [];
  for (var i=0,len=arr.length; i<len; i+=chunkSize)
    R.push(arr.slice(i,i+chunkSize));
  return R;
}
Answer

I'd prefer to use splice method:

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};
Answer

One-liner in ECMA 6

const [list,chuckSize] = [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 6]

new Array(Math.ceil(list.length / chuckSize)).fill().map(_ => list.splice(0,chuckSize))
Answer

Old question: New answer! I actually was working with an answer from this question and had a friend improve on it! So here it is:

Array.prototype.chunk = function ( n ) {
    if ( !this.length ) {
        return [];
    }
    return [ this.slice( 0, n ) ].concat( this.slice(n).chunk(n) );
};

[1,2,3,4,5,6,7,8,9,0].chunk(3);
> [[1,2,3],[4,5,6],[7,8,9],[0]]
Answer

Nowadays you can use lodash' chunk function to split the array into smaller arrays https://lodash.com/docs#chunk No need to fiddle with the loops anymore!

Answer

There have been many answers but this is what I use:

const chunk = (arr, size) =>
  arr
    .reduce((acc, _, i) =>
      (i % size)
        ? acc
        : [...acc, arr.slice(i, i + size)]
    , [])

// USAGE
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
chunk(numbers, 3)

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

First, check for a remainder when dividing the index by the chunk size.

If there is a remainder then just return the accumulator array.

If there is no remainder then the index is divisible by the chunk size, so take a slice from the original array (starting at the current index) and add it to the accumulator array.

So, the returned accumulator array for each iteration of reduce looks something like this:

// 0: [[1, 2, 3]]
// 1: [[1, 2, 3]]
// 2: [[1, 2, 3]]
// 3: [[1, 2, 3], [4, 5, 6]]
// 4: [[1, 2, 3], [4, 5, 6]]
// 5: [[1, 2, 3], [4, 5, 6]]
// 6: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 7: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 8: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 9: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
Answer

Ok, let's start with a fairly tight one:

function chunk(arr, n) {
    return arr.slice(0,(arr.length+n-1)/n|0).
           map(function(c,i) { return arr.slice(n*i,n*i+n); });
}

Which is used like this:

chunk([1,2,3,4,5,6,7], 2);

Then we have this tight reducer function:

function chunker(p, c, i) {
    (p[i/this|0] = p[i/this|0] || []).push(c);
    return p;
}

Which is used like this:

[1,2,3,4,5,6,7].reduce(chunker.bind(3),[]);

Since a kitten dies when we bind this to a number, we can do manual currying like this instead:

// Fluent alternative API without prototype hacks.
function chunker(n) {
   return function(p, c, i) {
       (p[i/n|0] = p[i/n|0] || []).push(c);
       return p;
   };
}

Which is used like this:

[1,2,3,4,5,6,7].reduce(chunker(3),[]);

Then the still pretty tight function which does it all in one go:

function chunk(arr, n) {
    return arr.reduce(function(p, cur, i) {
        (p[i/n|0] = p[i/n|0] || []).push(cur);
        return p;
    },[]);
}

chunk([1,2,3,4,5,6,7], 3);
Answer

I think this a nice recursive solution with ES6 syntax:

const chunk = function(array, size) {
  if (!array.length) {
    return [];
  }
  const head = array.slice(0, size);
  const tail = array.slice(size);

  return [head, ...chunk(tail, size)];
};

console.log(chunk([1,2,3], 2));

Answer

I aimed at creating a simple non-mutating solution in pure ES6. Peculiarities in javascript make it necessary to fill the empty array before mapping :-(

function chunk(a, l) { 
    return new Array(Math.ceil(a.length / l)).fill(0)
        .map((_, n) => a.slice(n*l, n*l + l)); 
}

This version with recursion seem simpler and more compelling:

function chunk(a, l) { 
    if (a.length == 0) return []; 
    else return [a.slice(0, l)].concat(chunk(a.slice(l), l)); 
}

The ridiculously weak array functions of ES6 makes for good puzzles :-)

Answer

Using generators

function* chunks(arr, n) {
 for(let i = 0; i < arr.length; i += n) {
     yield(arr.slice(i, i+n));
     }
}
let someArray = [0,1,2,3,4,5,6,7,8,9]
console.log([...chunks(someArray, 2)]) // [[0,1],[2,3],[4,5],[6,7],[8,9]]

Answer

If you use EcmaScript version >= 5.1, you can implement a functional version of chunk() using array.reduce() that has O(N) complexity:

function chunk(chunkSize, array) {
    return array.reduce(function(previous, current) {
        var chunk;
        if (previous.length === 0 || 
                previous[previous.length -1].length === chunkSize) {
            chunk = [];   // 1
            previous.push(chunk);   // 2
        }
        else {
            chunk = previous[previous.length -1];   // 3
        }
        chunk.push(current);   // 4
        return previous;   // 5
    }, []);   // 6
}

console.log(chunk(2, ['a', 'b', 'c', 'd', 'e']));
// prints [ [ 'a', 'b' ], [ 'c', 'd' ], [ 'e' ] ]

Explanation of each // nbr above:

  1. Create a new chunk if the previous value, i.e. the previously returned array of chunks, is empty or if the last previous chunk has chunkSize items
  2. Add the new chunk to the array of existing chunks
  3. Otherwise, the current chunk is the last chunk in the array of chunks
  4. Add the current value to the chunk
  5. Return the modified array of chunks
  6. Initialize the reduction by passing an empty array

Currying based on chunkSize:

var chunk3 = function(array) {
    return chunk(3, array);
};

console.log(chunk3(['a', 'b', 'c', 'd', 'e']));
// prints [ [ 'a', 'b', 'c' ], [ 'd', 'e' ] ]

You can add the chunk() function to the global Array object:

Object.defineProperty(Array.prototype, 'chunk', {
    value: function(chunkSize) {
        return this.reduce(function(previous, current) {
            var chunk;
            if (previous.length === 0 || 
                    previous[previous.length -1].length === chunkSize) {
                chunk = [];
                previous.push(chunk);
            }
            else {
                chunk = previous[previous.length -1];
            }
            chunk.push(current);
            return previous;
        }, []);
    }
});

console.log(['a', 'b', 'c', 'd', 'e'].chunk(4));
// prints [ [ 'a', 'b', 'c' 'd' ], [ 'e' ] ]

Answer

Created a npm package for this https://www.npmjs.com/package/array.chunk

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.slice(i, size + i));
}
return result;

When using a TypedArray

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.subarray(i, size + i));
}
return result;
Answer
in coffeescript:

b = (a.splice(0, len) while a.length)

demo 
a = [1, 2, 3, 4, 5, 6, 7]

b = (a.splice(0, 2) while a.length)
[ [ 1, 2 ],
  [ 3, 4 ],
  [ 5, 6 ],
  [ 7 ] ]
Answer
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}
Answer

The following ES2015 approach works without having to define a function and directly on anonymous arrays (example with chunk size 2):

[11,22,33,44,55].map((_, i, all) => all.slice(2*i, 2*i+2)).filter(x=>x.length)

If you want to define a function for this, you could do it as follows (improving on K._'s comment on Blazemonger's answer):

const array_chunks = (array, chunk_size) => array
    .map((_, i, all) => all.slice(i*chunk_size, (i+1)*chunk_size))
    .filter(x => x.length)
Answer

And this would be my contribution to this topic. I guess .reduce() is the best way.

var segment = (arr, n) => arr.reduce((r,e,i) => i%n ? (r[r.length-1].push(e), r)
                                                    : (r.push([e]), r), []),
        arr = Array.from({length: 31}).map((_,i) => i+1);
        res = segment(arr,7);
console.log(JSON.stringify(res));

But the above implementation is not very efficient since .reduce() runs through all arr function. A more efficient approach (very close to the fastest imperative solution) would be, iterating over the reduced (to be chunked) array since we can calculate it's size in advance by Math.ceil(arr/n);. Once we have the empty result array like Array(Math.ceil(arr.length/n)).fill(); the rest is to map slices of the arr array into it.

function chunk(arr,n){
  var r = Array(Math.ceil(arr.length/n)).fill();
  return r.map((e,i) => arr.slice(i*n, i*n+n));
}

arr = Array.from({length: 31},(_,i) => i+1);
res = chunk(arr,7);
console.log(JSON.stringify(res));

Answer

ES6 one-line approach based on Array.prototype reduce and push methods:

const doChunk = (list, size) => list.reduce((r, v) =>
  (!r.length || r[r.length - 1].length === size ?
    r.push([v]) : r[r.length - 1].push(v)) && r
, []);

console.log(doChunk([0,1,2,3,4,5,6,7,8,9,10,11,12], 5));
// [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12]]
Answer

ES6 Generator version

function* chunkArray(array,size=1){
    var clone = array.slice(0);
    while (clone.length>0) 
      yield clone.splice(0,size); 
};
var a = new Array(100).fill().map((x,index)=>index);
for(const c of chunkArray(a,10)) 
    console.log(c);
Answer

This is the most efficient and straight-forward solution I could think of:

function chunk(array, chunkSize) {
    let chunkCount = Math.ceil(array.length / chunkSize);
    let chunks = new Array(chunkCount);
    for(let i = 0, j = 0, k = chunkSize; i < chunkCount; ++i) {
        chunks[i] = array.slice(j, k);
        j = k;
        k += chunkSize;
    }
    return chunks;
}
Answer

ES6 spreads functional #ohmy #ftw

const chunk =
  (size, xs) => 
    xs.reduce(
      (segments, _, index) =>
        index % size === 0 
          ? [...segments, xs.slice(index, index + size)] 
          : segments, 
      []
    );

console.log( chunk(3, [1, 2, 3, 4, 5, 6, 7, 8]) );

Answer

Use chunk from lodash

lodash.chunk(arr,<size>).forEach(chunk=>{
  console.log(chunk);
})
Answer

EDIT: @mblase75 added more concise code to the earlier answer while I was writing mine, so I recommend going with his solution.

You could use code like this:

var longArray = ["Element 1","Element 2","Element 3", /*...*/];
var smallerArrays = []; // will contain the sub-arrays of 10 elements each
var arraySize = 10;
for (var i=0;i<Math.ceil(longArray.length/arraySize);i++) {
    smallerArrays.push(longArray.slice(i*arraySize,i*arraySize+arraySize));
}

Change the value of arraySize to change the maximum length of the smaller arrays.

Answer

Here is a non-mutating solution using only recursion and slice().

const splitToChunks = (arr, chunkSize, acc = []) => (
    arr.length > chunkSize ?
        splitToChunks(
            arr.slice(chunkSize),
            chunkSize,
            [...acc, arr.slice(0, chunkSize)]
        ) :
        [...acc, arr]
);

Then simply use it like splitToChunks([1, 2, 3, 4, 5], 3) to get [[1, 2, 3], [4, 5]].

Here is a fiddle for you to try out: https://jsfiddle.net/6wtrbx6k/2/

Answer

Here is neat & optimised implemention of chunk() function. Assuming default chunk size is 10.

var chunk = function(list, chunkSize) {
  if (!list.length) {
    return [];
  }
  if (typeof chunkSize === undefined) {
    chunkSize = 10;
  }

  var i, j, t, chunks = [];
  for (i = 0, j = list.length; i < j; i += chunkSize) {
    t = list.slice(i, i + chunkSize);
    chunks.push(t);
  }

  return chunks;
};

//calling function
var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var chunks = chunk(list);
Answer

I changed BlazeMonger's slightly to use for a jQuery object..

var $list = $('li'),
    $listRows = [];


for (var i = 0, len = $list.length, chunk = 4, n = 0; i < len; i += chunk, n++) {
   $listRows[n] = $list.slice(i, i + chunk);
}
Answer

I created the following JSFiddle to demonstrate my approach to your question.

(function() {
  // Sample arrays
  var //elements = ["0", "1", "2", "3", "4", "5", "6", "7"],
      elements = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43"];

  var splitElements = [],
      delimiter = 10; // Change this value as needed
      
  // parameters: array, number of elements to split the array by
  if(elements.length > delimiter){
  	splitElements = splitArray(elements, delimiter);
  }
  else {
  	// No need to do anything if the array's length is less than the delimiter
  	splitElements = elements;
  }
  
  //Displaying result in console
  for(element in splitElements){
  	if(splitElements.hasOwnProperty(element)){
    	console.log(element + " | " + splitElements[element]);
    }
  }
})();

function splitArray(elements, delimiter) {
  var elements_length = elements.length;

  if (elements_length > delimiter) {
    var myArrays = [], // parent array, used to store each sub array
      first = 0, // used to capture the first element in each sub array
      index = 0; // used to set the index of each sub array

    for (var i = 0; i < elements_length; ++i) {
      if (i % delimiter === 0) {
      	// Capture the first element of each sub array from the original array, when i is a modulus factor of the delimiter.
        first = i;
      } else if (delimiter - (i % delimiter) === 1) {
      // Build each sub array, from the original array, sliced every time the i one minus the modulus factor of the delimiter.
        index = (i + 1) / delimiter - 1;
        myArrays[index] = elements.slice(first, i + 1);
      }
      else if(i + 1 === elements_length){
      	// Build the last sub array which contain delimiter number or less elements
      	myArrays[index + 1] = elements.slice(first, i + 1);
      }
    }
    // Returned is an array of arrays
    return myArrays;
  }
}

First of all, I have two examples: an array with less than eight elements, another with an array with more than eight elements (comment whichever one you do not want to use).

I then check for the size of the array, simple but essential to avoid extra computation. From here if the array meets the criteria (array size > delimiter) we move into the splitArray function.

The splitArray function takes in the delimiter (meaning 8, since that is what you want to split by), and the array itself. Since we are re-using the array length a lot, I am caching it in a variable, as well as the first and last.

first represents the position of the first element in an array. This array is an array made of 8 elements. So in order to determine the first element we use the modulus operator.

myArrays is the array of arrays. In it we will store at each index, any sub array of size 8 or below. This is the key strategy in the algorithm below.

index represents the index for the myArrays variable. Every time a sub array of 8 elements or less is to be stored, it needs to be stored in the corresponding index. So if we have 27 elements, that means 4 arrays. The first, second and third array will have 8 elements each. The last will have 3 elements only. So index will be 0, 1, 2, and 3 respectively.

The tricky part is simply figuring out the math and optimizing it as best as possible. For example else if (delimiter - (i % delimiter) === 1) this is to find the last element that should go in the array, when an array will be full (example: contain 10 elements).

This code works for every single scenario, you can even change the delimiter to match any array size you'd like to get. Pretty sweet right :-)

Any questions? Feel free to ask in the comments below.

Answer

For a functional solution, using Ramda:

Where popularProducts is your input array, 5 is the chunk size

import splitEvery from 'ramda/src/splitEvery'

splitEvery(5, popularProducts).map((chunk, i) => {
// do something with chunk

})

Answer

Here's a recursive solution that is tail call optimize.

const splitEvery = (n, xs, y=[]) =>
  xs.length===0 ? y : splitEvery(n, xs.slice(n), y.concat([xs.slice(0, n)])) 

console.log(splitEvery(2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))

Answer

One more solution using arr.reduce():

const chunk = (arr, size) => (
  arr.reduce((acc, _, i) => {
    if (i % size === 0) acc.push(arr.slice(i, i + size))
    return acc
  }, [])
)

// Usage:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const chunked = chunk(numbers, 3)
console.log(chunked)

This solution is very similar to the solution by Steve Holgado. However, because this solution doesn't utilize array spreading and doesn't create new arrays in the reducer function, it's faster (see jsPerf test) and subjectively more readable (simpler syntax) than the other solution.

At every nth iteration (where n = size; starting at the first iteration), the accumulator array (acc) is appended with a chunk of the array (arr.slice(i, i + size)) and then returned. At other iterations, the accumulator array is returned as-is.

If size is zero, the method returns an empty array. If size is negative, the method returns broken results. So, if needed in your case, you may want to do something about negative or non-positive size values.


If speed is important in your case, a simple for loop would be faster than using arr.reduce() (see the jsPerf test), and some may find this style more readable as well:

function chunk(arr, size) {
  // This prevents infinite loops
  if (size < 1) throw new Error('Size must be positive')

  const result = []
  for (let i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size))
  }
  return result
}
Answer

    const array = [86,133,87,133,88,133,89,133,90,133];
    const new_array = [];

	const chunksize = 2;
    while (array.length) {
    	const chunk = array.splice(0,chunksize);
    	new_array.push(chunk);
    }

    console.log(new_array)

Answer

Using Array.prototype.splice() and splice it until the array has element.

Array.prototype.chunk = function(size) {
    let result = [];
    
    while(this.length) {
        result.push(this.splice(0, size));
    }
        
    return result;
}

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(arr.chunk(2));

Update

Array.prototype.splice() populates the original array and after performing the chunk() the original array (arr) becomes [].

So if you want to keep the original array untouched, then copy and keep the arr data into another array and do the same thing.

Array.prototype.chunk = function(size) {
  let data = [...this];  
  let result = [];
    
    while(data.length) {
        result.push(data.splice(0, size));
    }

    return result;
}

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log('chunked:', arr.chunk(2));
console.log('original', arr);

P.S: Thanks to @mts-knn for mentioning the matter.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.