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?
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.
const chunkSize = 10;
for (let i = 0; i < array.length; i += chunkSize) {
const chunk = array.slice(i, i + chunkSize);
// do whatever
}
The last chunk
may be smaller than chunkSize
. For example when given an array
of 12 elements the first chunk will have 10 elements, the second chunk only has 2.
Note that a chunkSize
of 0
will cause an infinite loop.
Here's a ES6 version using reduce
const perChunk = 2 // items per chunk const inputArray = ['a','b','c','d','e'] const 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
}, [])
You can use remainder operator to put consecutive items into different chunks:
const ch = (i % perChunk);
5/2 = 2.5
and Math.floor(2.5) = 2
so item with index 5 will placed in bucket 2
all
and one
here - makes reduce easier to read to my brain than other examples I've seen & used.
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));
}});
chunk
function on the array doesn't really outweigh the extra complexity you're adding and the subtle bugs that messing with built-in prototypes can cause.
array.map(function(i)
not array.map(function(elem,i)
though
array.myCompanyFlatten
, but please don’t add array.flatten
and pray that it’ll never cause issues. As you can see, mootools’ decision years ago now influences TC39 standards.
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;
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]]
function* chunks<T>(arr: T[], n: number): Generator<T[], void> {
and then use const foo = [ ...chunks( bar, 4 ) ];
- @RayFoss
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) {
if (chunkSize <= 0) throw "Invalid chunk size";
var R = [];
for (var i=0,len=arr.length; i<len; i+=chunkSize)
R.push(arr.slice(i,i+chunkSize));
return R;
}
function chunk<T>(array: T[], chunkSize: number): T[][] { const R = []; for (let i = 0, len = array.length; i < len; i += chunkSize) R.push(array.slice(i, i + chunkSize)); return R; }
chunkSize = 0
? Some valid function input should not stop the process.
Array<Array>
. And a non-positive chunk size does not make any sense. So throwing an error seems reasonable for me.
One-liner in ECMA 6
const [list,chunkSize] = [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 6]
[...Array(Math.ceil(list.length / chunkSize))].map(_ => list.splice(0,chunkSize))
list
array
.map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
[ ]
instead of new Array()
: [...Array(Math.ceil(list.length / chuckSize)].map(_ => list.splice(0,chuckSize))
var split=(fields,record=2)=>
\n fields.map((field,index,fields)=>
\n fields.splice(index,record,fields.slice(index,record)));
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;
};
var clone = [...array]
then do the lenght checking and splicing over that cloned array.
array = array.slice()
which also creates a shallow copy.
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!
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]]
.length
is much greater than the chunk-size n
). If this was a lazy language (unlike javascript), this algorithm would not suffer from O(N^2) time. That said, the recursive implementation is elegant. You can probably modify it to improve performance by first defining a helper function that recurses on array,position
, then dispatching: Array.prototype.chunk
returns [your helper function](...)
var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
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]]
One more solution using Array.prototype.reduce()
:
const chunk = (array, size) => array.reduce((acc, _, i) => { if (i % size === 0) acc.push(array.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 (array.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 reduce()
(see the jsPerf test), and some may find this style more readable as well:
function chunk(array, size) {
// This prevents infinite loops
if (size < 1) throw new Error('Size must be positive')
const result = []
for (let i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size))
}
return result
}
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));
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);
(p[i/n|0] || (p[i/n|0] = []))
, so you don't assign a value, if not necessary...
thisArg
= null
in your example chunker.bind(null, 3)
docs Function.prototype.bind()
ONE-LINER
const chunk = (a,n)=>[...Array(Math.ceil(a.length/n))].map((_,i)=>a.slice(n*i,n+n*i));
For TypeScript
const chunk = <T>(arr: T[], size: number): T[][] =>
[...Array(Math.ceil(arr.length / size))].map((_, i) =>
arr.slice(size * i, size + size * i)
);
DEMO
const chunk = (a,n)=>[...Array(Math.ceil(a.length/n))].map((_,i)=>a.slice(n*i,n+n*i)); document.write(JSON.stringify(chunk([1, 2, 3, 4], 2)));
Chunk By Number Of Groups
const part=(a,n)=>[...Array(n)].map((_,i)=>a.slice(i*Math.ceil(a.length/n),(i+1)*Math.ceil(a.length/n)));
For TypeScript
const part = <T>(a: T[], n: number): T[][] => {
const b = Math.ceil(a.length / n);
return [...Array(n)].map((_, i) => a.slice(i * b, (i + 1) * b));
};
DEMO
const part = (a, n) => {
const b = Math.ceil(a.length / n);
return [...Array(n)].map((_, i) => a.slice(i * b, (i + 1) * b));
};
document.write(JSON.stringify(part([1, 2, 3, 4, 5, 6], 2))+'
');
document.write(JSON.stringify(part([1, 2, 3, 4, 5, 6, 7], 2)));
chunk([1,2,3,4],2)
yields [ [ 1, 2 ], [ 3, 4 ], [] ]
. Doesn't seem right to me.
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 :-)
0
from the fill
, which makes the fill
look a little more sensible, imho.
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;
slice
method for TypedArray
, we can use subarray
instead developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
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.
console.log(arr);
to the end of your code snippet, it will log []
, i.e. arr
will be an empty array.
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)
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:
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 Add the new chunk to the array of existing chunks Otherwise, the current chunk is the last chunk in the array of chunks Add the current value to the chunk Return the modified array of chunks 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' ] ]
js
function splitToBulks(arr, bulkSize = 20) { const bulks = []; for (let i = 0; i < Math.ceil(arr.length / bulkSize); i++) { bulks.push(arr.slice(i * bulkSize, (i + 1) * bulkSize)); } return bulks; } console.log(splitToBulks([1, 2, 3, 4, 5, 6, 7], 3));
typescript
function splitToBulks<T>(arr: T[], bulkSize: number = 20): T[][] {
const bulks: T[][] = [];
for (let i = 0; i < Math.ceil(arr.length / bulkSize); i++) {
bulks.push(arr.slice(i * bulkSize, (i + 1) * bulkSize));
}
return bulks;
}
results = []
chunk_size = 10
while(array.length > 0){
results.push(array.splice(0, chunk_size))
}
The one line in pure javascript:
function chunks(array, size) { return Array.apply(0,{length: Math.ceil(array.length / size)}).map((_, index) => array.slice(index*size, (index+1)*size)) } // The following will group letters of the alphabet by 4 console.log(chunks([...Array(26)].map((x,i)=>String.fromCharCode(i + 97)), 4))
I recommend using lodash. Chunking is one of many useful functions there. Instructions:
npm i --save lodash
Include in your project:
import * as _ from 'lodash';
Usage:
const arrayOfElements = ["Element 1","Element 2","Element 3", "Element 4", "Element 5","Element 6","Element 7","Element 8","Element 9","Element 10","Element 11","Element 12"]
const chunkedElements = _.chunk(arrayOfElements, 10)
You can find my sample here: https://playcode.io/659171/
Here is an example where I split an array into chunks of 2 elements, simply by splicing chunks out of the array until the original array is empty.
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)
You can use the Array.prototype.reduce function to do this in one line.
let arr = [1,2,3,4]; function chunk(arr, size) { let result = arr.reduce((rows, key, index) => (index % size == 0 ? rows.push([key]) : rows[rows.length-1].push(key)) && rows, []); return result; } console.log(chunk(arr,2));
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));
So far so good but we can still simplify the above snipet further.
var chunk = (a,n) => Array.from({length: Math.ceil(a.length/n)}, (_,i) => a.slice(i*n, i*n+n)), arr = Array.from({length: 31},(_,i) => i+1), res = chunk(arr,7); console.log(JSON.stringify(res));
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 ] ]
Use chunk from lodash
lodash.chunk(arr,<size>).forEach(chunk=>{
console.log(chunk);
})
const array = ['a', 'b', 'c', 'd', 'e']; const size = 2; const chunks = []; while (array.length) { chunks.push(array.splice(0, size)); } console.log(chunks);
Success story sharing
chunk
being0
. (infinite loop)const array_chunks = (array, chunk_size) => Array(Math.ceil(array.length / chunk_size)).fill().map((_, index) => index * chunk_size).map(begin => array.slice(begin, begin + chunk_size));
.