Split array into chunks
Asked Answered
V

84

1032

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?

Vedi answered 13/12, 2011 at 20:24 Comment(6)
See also How to split a long array into smaller arrays and Split javascript array in chunks using underscore.js (as well as many of the dupes in the linked questions)Grouper
Possible duplicate of Splitting a JS array into N arraysGrazing
For lodash users, your are looking for _.chunk.Mauritamauritania
if you need minimum size of the last chunk also, here are the options: #57908633Swallow
I created a solution merged the best answers: https://mcmap.net/q/53160/-split-array-into-chunksGasworks
@webatrisans This answers your question. If you use the right TAGS then its easier for us to give the right pointers to the correct duplicateScoundrelly
S
1314

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.

Stoa answered 13/12, 2011 at 20:28 Comment(19)
Remember if this is a util function to assert against chunk being 0. (infinite loop)Sawyers
If the length of your array is not an even multiple of your chunk size, then this solution will "leave out" the remaining elements of your array. Right? For example, if chunk = 10 and your array has 12 elements then you will never //do whatever on elements 10 and 11.Diaphanous
Nope, the last chunk should just be smaller than the others.Stoa
@Blazemonger, indeed! Next time I will actually try it myself before jumping to conclusions. I assumed (incorrectly) that passing an input into array.slice that exceeded the bounds of the array would be a problem, but it works just perfect!Diaphanous
In coffeescript: Array::chunk = (chunkSize) -> array = this [].concat.apply [], array.map((elem, i) -> (if i % chunkSize then [] else [array.slice(i, i + chunkSize)]) )Parasitic
For one-liners (chain-lovers): 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));.Socalled
Why do you need j? First I thought it is an optimisation, but it is actually slower than for(i=0;i<array.length;i++){}Corfam
@Corfam It's a micro-optimization, caching the value of j (which can improve performance if j is large enough). In your code, array.length is being re-computed at every iteration (or at least, it was when I wrote this answer; I'm sure some JS engines have improved since then).Stoa
for ES6'ers ``` let myNewArray = [] const chunk = 9; for (let i=0; i<myNewArray.length; i+=chunk) { myNewArray = [...myNewArray, myOldFlattedArray.slice(i,i+chunk)]; } ```Conversazione
@Conversazione Why recreate the new array every single time? You can use push instedScummy
@l0rin because that way you're not changing the original array, you're creating a copy od it insteadConversazione
What is wrong with pushing into myNewArray instead of reassigning/spreading it in each loop cycle?Scummy
TS: export const chuncks = <T>(array: T[], size: number): T[][] => Array(Math.ceil(array.length / size)).fill(undefined) .map((_, index) => index * size).map(begin => array.slice(begin, begin + size));Incommunicado
Shouldn't you put i and j inside the parentheses as let?Gail
@Corfam when you insert inside the for loop the condition i<array.length the array.length part is being computed on every cicle of the loop. It's user readable friendly, but less efficient.Gail
@Stoa the problem of such micro-optimization is the variable terminology ;) You could have used const length inside the parentheses instead of j, since normally in computer science terminology j is only used when you have two loops, one inside the other. const does not pollute the other and outer scopes.Gail
I find other one line code const chunk = (arr, size) =>Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>arr.slice(i * size, i * size + size)); console.log(chunk([1, 2, 3, 4, 5], 2)); on linkDispersive
This won't process the last chunk unless it's exactly the same length as the chunkSize. Previous answers to this were incorrect, Nope, the last chunk should just be smaller than the others. is woefully false. Considering i += chunkSize, i < someArray.length will evaluate as false and will terminate the loop. I have tested this code. I can only imagine how many people have implemented this and are banging their heads against the table trusting these answers.Itagaki
What is array items are dynamic in nature. sometime you will receive all items so all chunks will have items. But some time main array might have fewer items and even half chunks are made. For example, array contains 100 items and chunk size is 10, now 10 chunks will be create properly. What if array contains only 33 items or may be less or more then 33 but lesser then 90?Bundle
G
312

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); 
Gynecoid answered 15/6, 2016 at 5:18 Comment(10)
This seems like the most condensed solution. What is chunkIndex = Math.floor(index/perChunk) getting ? Is it the average ?Islet
5/2 = 2.5 and Math.floor(2.5) = 2 so item with index 5 will placed in bucket 2Gynecoid
Thanks Andrei R. Ah so it steps through 0 - 2. So what is this called in Math terms? I guess my point is I would have never thought to divide index / 2 every index to get the index of the each slice. So I'm trying to wrap my head around it because I really like it but don't fully understand it in Math terms. I usually do this to get averages of a total number but this looks different.Islet
This solution is inefficient when compared to other solutions because you need to iterate over every element.Saltwater
you are right @JPdelaTorre, it's probably not as efficient as solutions that slice arrays, but you're splitting hairs here. most answers listed would be inefficient by that definition.Gynecoid
I like your use of all and one here - makes reduce easier to read to my brain than other examples I've seen & used.Saxophone
The second way is not working If I cut twice, and the count is too smallSharitasharity
Hot take from someone who loves functional programming, a for loop is more readable than reducing into a new arrayCzardas
Reading solutions like this I really wonder if people ever consider the space/time complexity of their algorithms anymore. concat() clones arrays, which means that not only does this algorithm iterate every element as @JPdelaTorre notices but it does so per every other element. With one million items (which is really not that weird for any real use-case) this algorithm takes nearly 22 seconds to run on my PC, while the accepted answer takes 8 milliseconds. Go team FP!Lombardi
const ch = (i % perChunk) seems better than Math.floor. Any reason for not being the "default" answer?Fireback
S
181

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]]

Can be typed with Typescript like so:

function* chunks<T>(arr: T[], n: number): Generator<T[], void> {
  for (let i = 0; i < arr.length; i += n) {
    yield arr.slice(i, i + n);
  }
}
Sotos answered 30/3, 2019 at 21:33 Comment(2)
How do you type this? typescriptlang.org/docs/handbook/release-notes/…Outsail
function* chunks<T>(arr: T[], n: number): Generator<T[], void> { and then use const foo = [ ...chunks( bar, 4 ) ]; - @RayFossAnabal
N
175

Modified from an answer by dbaseman: https://mcmap.net/q/54296/-javascript-to-return-a-new-array-of-paired-values-from-an-array-of-single-values-duplicate

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));
}});
Neurologist answered 4/5, 2012 at 21:57 Comment(9)
Eh, I'd avoid messing with the prototype as the feeling of coolness you get from calling the 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.Tammara
He's not messing with them he's extending them for Arrays. I understand never touching Object.prototype because that would bubble to all objects (everything) but for this Array specific function I don't see any issues.Aqueous
Pretty sure that should be array.map(function(i) not array.map(function(elem,i) thoughBak
Based on the compatibility chart on the mozilla dev site, Array.map for for IE9+. Be careful.Santossantosdumont
Be careful to pass floating - 7.5 type of numbers - lead to unpredictable chunksParasitic
If i have an array like [1,2,3,4] and i split it using 3, how can i get [[1,2,3], [4, 'something', 'something']] instead of [[1,2,3],[4]]?Dossier
@kaycee: You can use the for-loop method and special-case if (i==this.length-1), or more efficiently after the for loop, check if the last list of the list-of-lists has less than 3 length then add in the missing elements (beware the case where length==0). A good name for the something parameter might be 'fill', like [1,2,3,4].chunk(3, 'something') --> chunk(size, fill) {...}Neurologist
@Aqueous Here you go, here’s the issues this causes. Please NEVER modify native prototypes, especially without vendor prefix: developers.google.com/web/updates/2018/03/smooshgate It’s fine if you add 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.Barry
@Aqueous It's still dangerous because it may clash with what someone else has done, or with a future method. To be safe, just create a method that takes the array and avoid problems altogether. humanwhocodes.com/blog/2010/03/02/…Heptameter
I
151

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;
Insanitary answered 1/8, 2012 at 17:7 Comment(7)
"avoid mucking with native prototypes" new js developers should get a temporary, if not permanent, tattoo of this phrase.Nadda
I've been using javascript for years and spend next to no time bothering with prototype, at the very most calling functions, never modifying like you see some people do.Renault
the best suggestion in my eyes, the simplest to understand and in implementation, thank you very much!Excrescent
@JacobDalton I think it's all universities' fault. People think OOP must be used everywhere. So they are scared of "just creating a function". They want to be sure to put it inside something. Even if it's not appropriate at all. If there is no dot notation, there is no "architecture".Trudeau
@Trudeau I see this a lot too. I work in Laravel mostly and folks in my shop tend to create all sorts of manager classes in order to "stick to" OOP, but in doing so break the conventions of Laravel making coming into a project that much more complicated.Shreeves
Updated link to watch Andrew Dupont's JSConf 2011 talk: youtube.com/watch?v=xL3xCO7CLNMBorras
@Trudeau What does this have to do with OOP? Have you ever tried writing Excel formulas inline with a billion brackets and can't make heads or tails of it? JS prototypes are analogs to C# extension methods. Fluent code calling conventions makes it easy on old tired brains like myself that don't want to have to cut up each piece of code by hand to read it. I think the only problem with JS prototypes is that they can collide. If they were imported like C#'s then it would be perfectly fine. Aids code discoverability as well and creates a nice semantic API. LISP type bracket salad isn't fun.Finery
S
101

Splice version using ES6

let  [list,chunkSize] = [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 6];
list = [...Array(Math.ceil(list.length / chunkSize))].map(_ => list.splice(0,chunkSize))
console.log(list);
Spikelet answered 21/6, 2017 at 22:42 Comment(7)
it modifies the original list arrayPatricio
Easy fix using .slice().. .map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))Infusive
On JSPerf this is drastically more performant than many of the other answers.Xanthin
It's better to use [ ] instead of new Array(): [...Array(Math.ceil(list.length / chuckSize)].map(_ => list.splice(0,chuckSize))Classless
if you're splicing ahead, you may as well map the same array itself: var split=(fields,record=2)=>\n fields.map((field,index,fields)=>\n fields.splice(index,record,fields.slice(index,record)));Polloch
great example of recursion!Fireback
If you're chunking for presentation purposes, you will most likely want to avoid in-place mutation of the original model - here's a variation as a pure function using slice instead of splice: const chunk = (list, size) => [...Array(Math.ceil(list.length / size))].map((_, i) => list.slice(i * size, i * size + size))Usufruct
P
93

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;
}
Pacifism answered 16/7, 2014 at 13:24 Comment(5)
Thanks @Pacifism for making this benchmark: This was so helpful! I was using the splice approach and it crashed my Chrome v70 browser at a chunk size of 884432. With your suggested "slice" approach in place, my code doesn't crash the "render" process of the browser anymore. :)Punner
Here's a typescript version of this: 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; }Flosser
How long does it take for chunkSize = 0? Some valid function input should not stop the process.Herzog
@Herzog I've just added a condition when chunkSize is <= 0Pacifism
@Pacifism I am not sure, if returning the array unmodified is the best error handling.The expected return type of the function is Array<Array>. And a non-positive chunk size does not make any sense. So throwing an error seems reasonable for me.Herzog
I
46

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;
};
Ishmaelite answered 6/11, 2012 at 16:56 Comment(7)
Must be careful with this splice solution since it modifies the original array, so be sure to clearly document the side effects.Expel
Then use slice insteadGyrose
@Gyrose The result would be the same array over and over again when using slice. So it's not really a drop-in replacement without some more modifications.Lamm
The only thing that I would add to this answer is a clone to the original array. I would do that with ES6's spread operator. var clone = [...array] then do the lenght checking and splicing over that cloned array.Hofmannsthal
Or if you can't use ES6 features you can simply array = array.slice() which also creates a shallow copy.Afforest
This one is so much easier to work with, I like it. As long as you know how the methods work!Lucillalucille
My preference too much one drawback that if the remaining items is less than size, then they will be missed. suggest final check if original array not empty. push remaining items to resultsMalaguena
L
45

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!

Leontineleontyne answered 11/12, 2015 at 15:21 Comment(2)
I feel like there should be disclaimer to SO javascript questions: have you tried lodash? Pretty much the first thing I include in node or the browser.Nadda
Not everyone want/can include 3rd party librariesClaude
A
39

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]]
Aqueous answered 24/7, 2012 at 19:43 Comment(3)
fyi, the performance of this method is O(N^2), so it should not be used in performance-critical sections of code, or with long arrays (specifically, when the array's .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](...)Neurologist
thanks sensai for blessing me with your spirit that i must have chanelled tonightSeptemberseptembrist
or... var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }Allometry
S
32

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
}
Stilton answered 20/3, 2020 at 18:14 Comment(1)
your reduce example is by far the cleanest way of doing itCorfam
F
28

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]]
Frame answered 8/6, 2018 at 17:49 Comment(1)
Nice solution and nice visual representation of the iterations. I ended up with a very similar solution which I posted as an answer: stackoverflow.com/a/60779547Stilton
T
25

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));
Tartlet answered 16/10, 2018 at 8:59 Comment(0)
S
24

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))+'<br/>');
document.write(JSON.stringify(part([1, 2, 3, 4, 5, 6, 7], 2)));
Squirm answered 10/11, 2020 at 22:10 Comment(6)
Thanks! The oneliner is the best answer!Bradytelic
chunk([1,2,3,4],2) yields [ [ 1, 2 ], [ 3, 4 ], [] ]. Doesn't seem right to me.Quartic
Can't reproduce your results @HansBouwmeeste. u.davwheat.dev/3Om2Au5D.pngEccrinology
it was, but fixed now, my bad i should mentionSquirm
@David Wheatley. Confirmed. I tried the latest version and it works good now.Quartic
You should prefer the answer provided by @Benny Neugebauer though, for it does not create any intermediate array. This example here is actually creating two extra arrays. First is Array(n), second is due to [... ] wrapping the first one. The resulting array is neither of the two for map() is creating a third one. You could get rid of one array by using Array(n).fill(0) instead of [...Array(n)].Presidium
U
19

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);
Unsettle answered 26/3, 2014 at 0:17 Comment(4)
Doesnt Works in iE8.Klimt
HA! i love the kitten comment. sorry for no additional constructive input :)Ralleigh
I would do (p[i/n|0] || (p[i/n|0] = [])), so you don't assign a value, if not necessary...Anthozoan
for Currying (partial applying applied functions) you need in bind thisArg = null in your example chunker.bind(null, 3) docs Function.prototype.bind()Singlet
I
14

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 :-)

Indifference answered 3/11, 2017 at 23:25 Comment(1)
I also wrote mine much like this. It still works if you remove the 0 from the fill, which makes the fill look a little more sensible, imho.Montane
I
12

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;
Inverse answered 18/1, 2016 at 4:39 Comment(1)
@A1rPun My bad, I didn't add comment there. Yea, there is no slice method for TypedArray, we can use subarray instead developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Inverse
E
10

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)
Express answered 16/10, 2018 at 20:35 Comment(1)
- short and niceUndergrowth
A
10

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.

Agnola answered 24/4, 2020 at 16:39 Comment(1)
Note that splicing modifies the original array. If you add console.log(arr); to the end of your code snippet, it will log [], i.e. arr will be an empty array.Stilton
K
10

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/

Krilov answered 26/8, 2020 at 15:37 Comment(0)
A
9

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' ] ]
Alvaroalveolar answered 30/5, 2016 at 19:9 Comment(0)
S
9

Use chunk from lodash

lodash.chunk(arr,<size>).forEach(chunk=>{
  console.log(chunk);
})
Saphena answered 16/9, 2019 at 15:32 Comment(0)
P
9

You can take this ES6 chunk function, which is easy to use:

const chunk = (array, size) =>
  Array.from({length: Math.ceil(array.length / size)}, (value, index) => array.slice(index * size, index * size + size));

const itemsPerChunk = 3;
const inputArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];

const newArray = chunk(inputArray, itemsPerChunk);
console.log(newArray.length); // 3,

document.write(JSON.stringify(newArray)); //  [ [ 'a', 'b', 'c' ], [ 'd', 'e', 'f' ], [ 'g' ] ]
Punner answered 1/2, 2021 at 16:34 Comment(1)
Love this one-liner mostly for it doesn't create any intermediate helper array but some rather small object.Presidium
H
9

const array = ['a', 'b', 'c', 'd', 'e'];
const size = 2;
const chunks = [];
while (array.length) {
    chunks.push(array.splice(0, size));
}
console.log(chunks);
Headstall answered 24/6, 2021 at 11:26 Comment(0)
D
9

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;
}
Dreiser answered 15/7, 2021 at 14:8 Comment(0)
N
8
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}
Neoplasm answered 6/1, 2017 at 9:12 Comment(2)
Not sure why this was down-voted, but the code could use some explanation.Rainproof
Because splice is destructive to original array.Renayrenckens
P
8

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)
Preexist answered 11/2, 2020 at 0:58 Comment(1)
While this might answer the question, a bit of explanation would be extremely helpful, click edit and please type in some explanation.Nika
T
8

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))
Thomas answered 8/6, 2020 at 19:4 Comment(0)
H
8

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));
Hebrews answered 26/4, 2021 at 8:9 Comment(0)
B
7
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 ] ]
Bradeord answered 11/4, 2014 at 9:50 Comment(2)
a.splice(0, 2) removes the subarray of a[0..1] from a and returns the subarray a[0..1]. I am making an array of all those arraysBradeord
I recommend using the non-destructive slice() method instead of splice()Kerianne
H
7

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));
Hooten answered 22/5, 2017 at 9:4 Comment(0)
W
7

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]))
Wellbeloved answered 25/6, 2019 at 23:27 Comment(0)
L
6

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);
Lentz answered 27/12, 2016 at 16:41 Comment(1)
Use const instead.Socalled
M
6

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

})
Mondragon answered 29/9, 2017 at 14:52 Comment(0)
B
6

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]) );
Bijugate answered 11/10, 2018 at 7:5 Comment(0)
C
6

If you are using Underscore JS, just use :

var result = _.chunk(arr,elements_per_chunk)

Most projects already use underscore as a dependency, anyways.

Clicker answered 3/5, 2020 at 19:32 Comment(1)
I was about to post same answer when I saw your'sBuke
E
6

With mutation of the source array:

let a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ], aa = [], x
while((x = a.splice(0, 2)).length) aa.push(x)

// aa == [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ], [ 9 ] ]
// a == []

Without mutating the source array:

let a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ], aa = []
for(let i = 0; i < a.length; i += 2) aa.push(a.slice(i, i + 2))

// aa == [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ], [ 9 ] ]
// a == [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Emmeram answered 10/7, 2022 at 12:23 Comment(0)
S
5

A ES2020 one-liner with a nullish coalescing assignment:

const arr = '1234567890'.split('');
const chunkSize = 3;
const r = arr.reduce((arr, item, idx) => (arr[idx / chunkSize | 0] ??= []).push(item) && arr, []);

console.log(JSON.stringify(r));
Shoe answered 22/6, 2023 at 0:59 Comment(0)
A
4

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]]
Acromegaly answered 16/12, 2017 at 13:32 Comment(0)
D
4

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;
}
Dee answered 27/3, 2018 at 0:7 Comment(0)
P
4

This is what i use, it might not be super fast, but it is compact and simple:

let chunksplit = (stream, size) => stream.reduce((chunks, item, idx, arr) => (idx % size == 0) ? [...chunks, arr.slice(idx, idx + size)] : chunks, []);
//if the index is a multiple of the chunksize, add new array

let testArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22];

document.write(JSON.stringify( chunksplit(testArray, 5) ));
//using JSON.stringify for the nested arrays to be shown
Persson answered 24/10, 2019 at 20:40 Comment(0)
M
4

example with unchanged source array
and not making all the chunks at once. (memory saver!)

const array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21];

const chunkSize = 4
for (var i = 0; i < array.length; i += chunkSize) {
    const chunk = array.slice(i, i + chunkSize);
    console.log('chunk=',chunk)
    // do whatever
}
console.log('src array didnt changed. array=',array)
Mammon answered 12/7, 2021 at 9:26 Comment(0)
P
3

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.

Pierrepierrepont answered 13/12, 2011 at 20:48 Comment(0)
A
3

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/

Affirmative answered 9/2, 2017 at 12:7 Comment(0)
C
3

I tried a recursive function…

const chunk = (arr, n) =>
    arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : [];

…which is nice and short, but seems to take about 256× as long as @AymKdn’s answer for 1,000 elements, and 1,058× as long for 10,000 elements!

Clearcole answered 21/1, 2022 at 2:36 Comment(0)
C
2

Here's my approach using Coffeescript list comprehension. A great article detailing comprehensions in Coffeescript can be found here.

chunk: (arr, size) ->
    chunks = (arr.slice(index, index+size) for item, index in arr by size)
    return chunks
Cordite answered 27/5, 2016 at 22:19 Comment(0)
A
2

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);
Allerie answered 1/6, 2017 at 13:13 Comment(0)
B
2

This should be straightforward answer without many mathematical complications.

function chunkArray(array, sizeOfTheChunkedArray) {
  const chunked = [];

  for (let element of array) {
    const last = chunked[chunked.length - 1];

    if(!last || last.length === sizeOfTheChunkedArray) {
      chunked.push([element])
    } else {
      last.push(element);
    }
  }
  return chunked;
}
Broderic answered 7/8, 2018 at 9:37 Comment(0)
H
2

The most efficient way is to treat the array as Iterable, and do lazy pagination. That way, it will produce data only when requested. The code below uses operator page from iter-ops library:

import {pipe, page} from 'iter-ops';

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; // some input data

const i = pipe(arr, page(2)); //=> Iterable<number>

console.log(...i); //=> [ 1, 2 ] [ 3, 4 ] [ 5, 6 ] [ 7, 8 ] [ 9 ]

Works the same way for any Iterable or AsyncIterable.


P.S I'm the author of the library.

Hotbox answered 25/11, 2022 at 13:50 Comment(0)
M
1

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.

Mcnutt answered 14/1, 2016 at 18:11 Comment(0)
F
1

I just wrote this with the help of a groupBy function.

// utils
const group = (source) => ({
  by: (grouping) => {
    const groups = source.reduce((accumulator, item) => {
      const name = JSON.stringify(grouping(item));
      accumulator[name] = accumulator[name] || [];
      accumulator[name].push(item);
      return accumulator;
    }, {});

    return Object.keys(groups).map(key => groups[key]);
  }
});

const chunk = (source, size) => group(source.map((item, index) => ({ item, index })))
.by(x => Math.floor(x.index / size))
.map(x => x.map(v => v.item));


// 103 items
const arr = [6,2,6,6,0,7,4,9,3,1,9,6,1,2,7,8,3,3,4,6,8,7,6,9,3,6,3,5,0,9,3,7,0,4,1,9,7,5,7,4,3,4,8,9,0,5,1,0,0,8,0,5,8,3,2,5,6,9,0,0,1,5,1,7,0,6,1,6,8,4,9,8,9,1,6,5,4,9,1,6,6,1,8,3,5,5,7,0,8,3,1,7,1,1,7,6,4,9,7,0,5,1,0];

const chunks = chunk(arr, 10);

console.log(JSON.stringify(chunks));
Feil answered 25/5, 2016 at 1:55 Comment(0)
M
1

Hi try this -

 function split(arr, howMany) {
        var newArr = []; start = 0; end = howMany;
        for(var i=1; i<= Math.ceil(arr.length / howMany); i++) {
            newArr.push(arr.slice(start, end));
            start = start + howMany;
            end = end + howMany
        }
        console.log(newArr)
    }
    split([1,2,3,4,55,6,7,8,8,9],3)
Malchy answered 29/8, 2018 at 4:48 Comment(0)
S
1

Here's a version with tail recursion and array destructuring.

Far from the fastest performance, but I'm just amused that js can do this now. Even if it isn't optimized for it :(

const getChunks = (arr, chunk_size, acc = []) => {
    if (arr.length === 0) { return acc }
    const [hd, tl] = [ arr.slice(0, chunk_size), arr.slice(chunk_size) ]
    return getChunks(tl, chunk_size, acc.concat([hd]))
}

// USAGE
const my_arr = [1,2,3,4,5,6,7,8,9]
const chunks = getChunks(my_arr, 2)
console.log(chunks) // [[1,2],[3,4], [5,6], [7,8], [9]]
Sheriff answered 23/9, 2018 at 19:49 Comment(0)
H
1

There could be many solution to this problem.

one of my fav is:

function chunk(array, size) {
    const chunked = [];

    for (element of array){
        let last = chunked[chunked.length - 1];

        if(last && last.length != size){
            last.push(element)
        }else{
            chunked.push([element])
        }
    }
   
    return chunked;
}


function chunk1(array, size) {
    const chunked = [];

    let index = 0;

    while(index < array.length){
        chunked.push(array.slice(index,index+ size))
        index += size;
    }
    return chunked;
}

console.log('chunk without slice:',chunk([1,2,3,4,5,5],2));
console.log('chunk with use of slice funtion',chunk1([1,2,3,4,5,6],2))
Hippogriff answered 3/1, 2020 at 13:23 Comment(0)
N
1

In case this is useful to anyone, this can be done very simply in RxJS 6:

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
from(arr).pipe(bufferCount(3)).subscribe(chunk => console.log(chunk));

Outputs: [1, 2, 3] [4, 5, 6] [7, 8, 9] [10, 11, 12] [13, 14, 15] [16]

Nicaea answered 17/9, 2020 at 0:24 Comment(0)
V
1

Late, here's my two cents. Like many said, I first would think of something like

chunker = (a,n) => [...Array(Math.ceil(a.length/n))].map((v,i) => a.slice(i*n, (i+1)*n))

But what I like better and haven't seen here is this:

chunker = (n) => (r,v,i) => (c = Math.floor(i/n), (r[c] = r[c] || []).push(v), r)

console.log(arr.reduce(chunker(3), []))

with longer variant

chunker = (a, n) => a.reduce((r,v,i) => {
  c = Math.floor(i/n); // which chunk it belongs to
  (r[c] = r[c] || []).push(v)
  return r
}, [])

console.log(chunker(arr, 3))

Explanations

  1. The common answer will first determine the number of chunks and then get slices of the original array based on what chunk is at and the size of each chunks

  2. The chunker reducer function will go through each element and put it in the according evaluated chunk's array.

The performance is almost the same, the reduce method being 4% slower on average for what I could see.

PS: reduce(ing) has the advantage of easily change grouping criteria. In the question and examples the criteria is adjacent cells (and mapping uses slice for that). But you could want to do it in "cycles" for example, using mod (% operator), or any other math formulas

Re-reading it made me see that the formula could be a parameter too, leading to a more general solution and requiring 2 functions to achieve the answer:

splitter = (a, f) => a.reduce((r,v,i) => { // math formula and/or function
  c = f(v, i) || 0; // custom formula, receiving each value and index
  (r[c] = r[c] || []).push(v)
  return r
}, [])

chunker = (a, n) => splitter(a, (v,i) => Math.floor(i/n))

console.log(chunker(arr, 3))
console.log(splitter(arr, (v,i) => v % 2))  // is it even or odd?

With little changes splitter could also be used for making named-arrays aka objects, with function returning strings instead of numbers :)

Valid answered 6/4, 2021 at 8:12 Comment(0)
G
1

An efficient solution is to join solution with slice and push by indexChunk, the solution split into chunks:

function splitChunks(sourceArray, chunkSize) {
  if(chunkSize <= 0)
    throw "chunkSize must be greater than 0";
  let result = [];
  for (var i = 0; i < sourceArray.length; i += chunkSize) {
    result[i / chunkSize] = sourceArray.slice(i, i + chunkSize);
  }
  return result;
}

let ar1 = [
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
];

console.log("Split in chunks with 4 size", splitChunks(ar1, 4));
console.log("Split in chunks with 7 size", splitChunks(ar1, 7));
Gasworks answered 15/3, 2022 at 14:14 Comment(0)
G
1

The problem with the current top answers is that they produce lopsided chunks. For example, the currently accepted answer will distribute a 101-element array into 10 chunks of size 10, followed by 1 chunk of size 1.

Using some modular arithmetic can create uniform chunk sizes that never differ by more than 1:

function split_array(a, nparts) {
  const quot = Math.floor(a.length / nparts)
  const rem = a.length % nparts
  var parts = []
  for (var i = 0; i < nparts; ++i) {
    const begin = i * quot + Math.min(rem, i)
    const end = begin + quot + (i < rem)
    parts.push(a.slice(begin, end))
  }
  return parts
}

var chunks = split_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3)
console.log(JSON.stringify(chunks))

Output:

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

(Copied from a related answer.)

Globular answered 5/7, 2022 at 15:56 Comment(0)
L
1

I could not find an answer here that made sure the chunks were equal despite an odd size of elements. Therefore, I wrote my own method to do this. This ensures chunks are always of size, where the filled in holes are of the provided default value. This also does not modify the original array.

Modern version:

// Modern version

function chunkArray(a, s, d) {
    const l = a.length;
    let p = 0;
    if (l !== 0) {
        return a.reduce((a, c, i) => {
            if ((i % s) === 0)
                p = a.push([]);
            let r = a[p - 1].push(c);
            if ((i + 1) === l)
                while (r < s)
                    r = a[p - 1].push(d);
            return a;
        }, []);
    } else
        return [...Array(s).fill(d)];
}

const add = (v, i) => v + (i + 1);
console.log('a.length = 7, s = 3, d = 0');
console.log(chunkArray([...Array(7).fill(0)].map(add), 3, 0));
console.log('');
console.log('a.length = 12, s = 2, d = 2');
console.log(chunkArray([...Array(12).fill(0)].map(add), 2, 2));
console.log('');
console.log('a.length = 10, s = 6, d = "ADDITIONAL"');
console.log(chunkArray([...Array(10).fill('ORIGINAL')].map(add), 6, 'ADDITIONAL'));
console.log('');
console.log('a.length = 20, s = 12, d = undefined');
console.log(chunkArray([...Array(20).fill(0)].map(add), 12, undefined));
console.log('');
console.log('a.length = 30, s = 4, d = null');
console.log(chunkArray([...Array(30).fill('TEST')].map(add), 4, null));

IE10+ compatible version:

// IE10+ compatible version

function addArray(a) {
    return a.map(function(v, i) { return v + (i + 1); });
}

function createArray(s, d) {
    var a = [];
    for (var i = 0; i < s; i++)
        a.push(d);
    return a;
}

function chunkArray(a, s, d) {
    var l = a.length, p = 0, r = 0;
    if (l !== 0) {
        return a.reduce(function(a, c, i) {
            if ((i % s) === 0)
                p = a.push([]);
            r = a[p - 1].push(c);
            if ((i + 1) === l)
                while (r < s)
                    r = a[p - 1].push(d);
            return a;
        }, []);
    } else
        return createArray(s, d);
}

console.log('a.length = 7, s = 3, d = 0');
console.log(chunkArray(addArray(createArray(7, 0)), 3, 0));
console.log('');
console.log('a.length = 12, s = 2, d = 2');
console.log(chunkArray(addArray(createArray(12, 0)), 2, 2));
console.log('');
console.log('a.length = 10, s = 6, d = "ADDITIONAL"');
console.log(chunkArray(addArray(createArray(10, 'ORIGINAL')), 6, 'ADDITIONAL'));
console.log('');
console.log('a.length = 20, s = 12, d = undefined');
console.log(chunkArray(addArray(createArray(20, 0)), 12, undefined));
console.log('');
console.log('a.length = 30, s = 4, d = null');
console.log(chunkArray(addArray(createArray(30, 'TEST')), 4, null));
Later answered 6/5, 2023 at 4:11 Comment(0)
P
1

Ok, this is kind of a slightly more enhanced version of Ikechukwu Eze answer using generators.

It's updated so the source doesn't have to be an array, but any iterable.

The main benefits of using generators & iterables is that they can work with more than just arrays, (strings, DOM elements etc, custom iterator) and the memory usage can be kept much lower, and of course code re-use. It's also possible to use custom generators that you can chain.

eg..

function *chunkIterator(iterable, chunkSize) {
  let i, iter = iterable[Symbol.iterator]();
  function *nextChunk() {
    for (let l = 0; l < chunkSize && !i.done; l++) {
      yield i.value;
      i = iter.next();
    }
  }
  i = iter.next();
  while (!i.done) yield [...nextChunk()];
}


const myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const chunkedArray = [...chunkIterator(myArray, 3)];
console.log(JSON.stringify(chunkedArray));

for (const chunk of chunkIterator('hello world!.', 3)) 
  console.log(chunk.join(''));

It's also possible to make the chunks iterable too, this means that no arrays even needs creating. Also because the chunks can be any iterable, I've create a simple random number generator instead of supplying an array.

example ->

function *chunkIterator(iterable, chunkSize) {
  let i, iter = iterable[Symbol.iterator]();
  function *nextChunk() {
    for (let l = 0; l < chunkSize && !i.done; l++) {
      yield i.value;
      i = iter.next();
    }
  }
  i = iter.next();
  while (!i.done) yield nextChunk();
}

function *rand() {
  for (let l = 0; l < 10; l++) 
    yield `${l} = ${(Math.random()*1000) | 0}`;
}

for (const r of chunkIterator(rand(), 3)) {
  console.log('---');
  for (const c of r) {
    console.log(c);
  }
}
Portauprince answered 13/6, 2023 at 13:5 Comment(0)
R
1

Recursive way

function chunk(array, chunk_size){
    if(array.length == 0) return [];
    else return [array.splice(0, chunk_size)].concat(chunk(array, chunk_size))
}

console.log(chunk([1,2,3,4,5,6,7,8],3))
Rhoden answered 6/7, 2023 at 13:29 Comment(0)
U
1

Here is a simple solution, based on flatMap (I haven't see any in the existing answers):

function splitIntoChunks(array, chunkSize) {
  return array.flatMap((x, i) =>
    i % chunkSize === 0 ? [array.slice(i, i + chunkSize)] : []
  );
}
Ulla answered 5/10, 2023 at 7:51 Comment(0)
G
1

Another version using Array.reduce but without using Math object and remainder (%) operator:

const chunk = size => array => array.reduce((result, item) => {
  if (result[result.length - 1].length < size) {
    result[result.length - 1].push(item);
  } else {
    result.push([item]);
  }
  return result;
}, [[]]);

const myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

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

Guendolen answered 27/10, 2023 at 7:21 Comment(0)
M
0

function groupArr(arr = [], size = 0) {
    if (!Array.isArray(arr) || !arr.length) return [];
    if (arr.length <= size || size <= 0) return [arr];

    const resultArr = [];

    for (let i = 0, j = size, len = arr.length; i < len; j += size) {
        let tempArr = [];

        if (j > len) {
            tempArr = arr.slice(i);
        } else {
            tempArr = arr.slice(i, j);
        }
        i = j;
        resultArr.push(tempArr);
    }

    return resultArr;
}

let arr = [1, 2, 3, 4, 5];
let size = 2;
let result = groupArr(arr, size);

console.log(result);
Maritsa answered 13/12, 2011 at 20:24 Comment(0)
N
0
# in coffeescript
# assume "ar" is the original array
# newAr is the new array of arrays

newAr = []
chunk = 10
for i in [0... ar.length] by chunk
   newAr.push ar[i... i+chunk]

# or, print out the elements one line per chunk
for i in [0... ar.length] by chunk
   console.log ar[i... i+chunk].join ' '
Norword answered 15/7, 2015 at 22:8 Comment(0)
E
0

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);
}
Emaciation answered 11/12, 2015 at 14:42 Comment(0)
S
0

Here's a solution using ImmutableJS, where items is an Immutable List and size is the required grouping size.

const partition = ((items, size) => {
  return items.groupBy((items, i) => Math.floor(i/size))
})
Seema answered 7/11, 2016 at 1:4 Comment(0)
D
0

Her is a simple solution using @Blazemonger solution

function array_chunk(arr, size){
    // initialize vars
    var i,
    j = arr.length,
    tempArray = [];
    // loop through and jump based on size
    for (i=0; i<j; i+=size) {
        // slice chunk of arr and push to tempArray
        tempArray.push(arr.slice(i,i+size));
    }
    // return temp array (chunck)
    return tempArray
}

This got the pipline flowing for me, hope this helps someone else out there. :)

Disclaim answered 11/2, 2017 at 13:20 Comment(0)
O
0

Here's another solution with the reduce() method, though slightly different from other examples. Hopefully my explanation is a bit clearer as well.

var arr = [0, 1, 2, 3, 4, 5, 6, 7];
var chunkSize = 3;

arr = arr.reduce((acc, item, idx) => {
  let group = acc.pop();
  if (group.length == chunkSize) {
    acc.push(group);
    group = [];
  }
  group.push(item);
  acc.push(group);
  return acc;
}, [[]]);

console.log(arr); //Prints [[0, 1, 2], [3, 4, 5], [6, 7]]

Explanation

We call a reducer which, for each item in the array, gets the last item of the accumulator with pop(). Remember that this item is an array which groups up to chunkSize number of items (3 in this example).

If, and only if, this group has the array length equal to chunksize we need to re-insert the group back into the accumulator and create a new group.

We then push the current item into our group array (which may already contain 0, 1 or 2 items from the previous steps). With the current item inserted into the group, we need to re-insert the group back into the larger collection.

The process will repeat until we've iterated through all items in arr.

Note that we have also provided the reducer with the starting value of an empty array inside an array with [[]].

Oto answered 9/5, 2017 at 15:1 Comment(0)
B
0

Try this :

var oldArray =  ["Banana", "Orange", "Lemon", "Apple", "Mango", "Banana", "Orange", "Lemon", "Apple", "Mango", "Banana", "Orange", "Lemon", "Apple", "Mango", "Banana", "Orange", "Lemon", "Apple", "Mango", "Banana", "Orange", "Lemon", "Apple", "Mango"];

var newArray = [];

while(oldArray.length){
    let start = 0;
    let end = 10;
    newArray.push(oldArray.slice(start, end));
    oldArray.splice(start, end);
 }
 
 console.log(newArray);
Bedell answered 3/7, 2018 at 13:27 Comment(0)
R
0

I prefer to use the splice method instead of slice. This solution uses the array length and chunk size to create a loop count and then loops over the array which gets smaller after every operation due to splicing in each step.

    function chunk(array, size) {
      let resultArray = [];
      let chunkSize = array.length/size;
      for(i=0; i<chunkSize; i++) {
        resultArray.push(array.splice(0, size));
      }
    return console.log(resultArray);
    }
    chunk([1,2,3,4,5,6,7,8], 2);

If you dont want to mutate the original array, you can clone the original array using the spread operator and then use that array to solve the problem.

    let clonedArray = [...OriginalArray]
Rolph answered 21/7, 2018 at 15:33 Comment(0)
C
0

my trick is to use parseInt(i/chunkSize) and parseInt(i%chunkSize) and then filling the array

// filling items
let array = [];
for(let i = 0; i< 543; i++)
  array.push(i);
 
 // printing the splitted array
 console.log(getSplittedArray(array, 50));
 
 // get the splitted array
 function getSplittedArray(array, chunkSize){
  let chunkedArray = [];
  for(let i = 0; i<array.length; i++){
    try{
      chunkedArray[parseInt(i/chunkSize)][parseInt(i%chunkSize)] = array[i];
    }catch(e){
      chunkedArray[parseInt(i/chunkSize)] = [];
      chunkedArray[parseInt(i/chunkSize)][parseInt(i%chunkSize)] = array[i];
    }
  }
  return chunkedArray;
 }
Consueloconsuetude answered 17/4, 2019 at 16:54 Comment(0)
Z
0

I solved it like this:

const chunks = [];
const chunkSize = 10;
for (let i = 0; i < arrayToSplit.length; i += chunkSize) {
  const tempArray = arrayToSplit.slice(i, i + chunkSize);
  chunks.push(tempArray);
}
Zealot answered 1/12, 2020 at 14:5 Comment(0)
G
0

Super late to the party but I solved a similar problem with the approach of using .join("") to convert the array to one giant string, then using regex to .match(/.{1,7}/) it into arrays of substrings of max length 7.

const arr = ['abc', 'def', 'gh', 'ijkl', 'm', 'nopq', 'rs', 'tuvwx', 'yz'];
const arrayOfSevens = arr.join("").match(/.{1,7}/g);
// ["abcdefg", "hijklmn", "opqrstu", "vwxyz"]

Would be interesting to see how this performs in a speed test against other methods

Goldenseal answered 1/2, 2021 at 22:45 Comment(2)
This doesn't serve the original purpose at all.Shay
It would perform quite poorly I'm afraid because of, first, Regexes then the join, I'd say.Patin
M
0

TypeScript version. Demonstrated is 101 random uid's split into groups of 10

const idArrayLengthLimit = 10;
const randomOneHundredOneIdArray = Array
    .from(Array(101).keys())
    .map(() => generateUid(5));

function generateUid(length: number) {
  const uidString: string[] = [];
  const uidChars = 'abcdefghijklmnopqrstuvwxyz0123456789';
  for (let i = 0; i < length; i++) {
    uidString
      .push(uidChars.charAt(Math.floor(Math.random() * uidChars.length)));
  }
  return uidString.join('');
}

for (let i = 0; i < randomOneHundredOneIdArray.length; i++) {
 if(i % idArrayLengthLimit === 0){
     const result = randomOneHundredOneIdArray
       .filter((_,id) => id >= i && id < i + idArrayLengthLimit);
    // Observe result
    console.log(result);
 }
}
Minaret answered 15/5, 2021 at 3:18 Comment(0)
P
0

Here's a more specific case that someone might find valuable. I haven't seen it mentioned here yet.

What if you don't want constant/even chunk sizes, and instead want to specify the indices where the array is split. In that case, you can use this:

const splitArray = (array = [], splits = []) => {
  array = [...array]; // make shallow copy to avoid mutating original
  const chunks = []; // collect chunks
  for (const split of splits.reverse()) chunks.push(array.splice(split)); // go backwards through split indices and lop off end of array
  chunks.push(array); // add last remaining chunk (at beginning of array)
  return chunks.reverse(); // restore chunk order
};

Then:

splitArray([1, 2, 3, 4, 5, 6, 7, 8, 9], [4, 6]) 
// [ [1, 2, 3, 4] , [5, 6] , [7, 8, 9] ]

Note that this will do funny things if you give it non-ascending/duplicate/negative/non-integer/etc split indices. You could add checks for these edge cases (e.g. Array.from(new Set(array)) to de-duplicate.

Pediculosis answered 7/1, 2022 at 1:23 Comment(0)
I
0

My favorite is the generator generateChunks with the additional function getChunks to execute the generator.

function* generateChunks(array, size) {
    let start = 0;
    while (start < array.length) {
        yield array.slice(start, start + size);
        start += size;
    }
}

function getChunks(array, size) {
    return [...generateChunks(array, size)];
}

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

As an addition here the generator generatePartitions with the further function getPartitions to get n arrays of equal size.

function generatePartitions(array, count) {
    return generateChunks(array, Math.ceil(array.length / count));
}

function getPartitions(array, count) {
    return [...generatePartitions(array, count)];
}

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

An advantage of the generator compared to many other solutions is that not multiple unnecessary arrays are created.

Imply answered 17/5, 2022 at 20:31 Comment(0)
J
0

In JS,

const splitInChunks = (arr,n) => {
  let chunksArr = [];
  if(arr !=null && arr!= undefined){
      for(i=0; i<arr.length;i+=n){
      if(arr.length-i>=n)
          chunksArr.push(arr.slice(i,i+n))
      else
           chunksArr.push(arr.slice(i,arr.length))
  }
  return chunksArr
  }
}
Jejune answered 26/6, 2022 at 12:11 Comment(0)
H
0

Optimized function with one loop in JS, no slice, reduce or anything

const splitArrayIntoN = (myArray, n) => {
  if (!Number.isSafeInteger(n)) {
    return myArray;
  }
  let count = 0;
  const tempArray = [];
  for (let item = 0; item < myArray.length; item++) {
    if (tempArray[count] && tempArray[count].length !== n) {
      tempArray[count] = [...tempArray[count], myArray[item]];
    }
    if (!tempArray[count]) {
      tempArray[count] = [myArray[item]];
    }
    if (tempArray[count].length === n) {
      count++;
    }
  }
  return tempArray;
};
Horary answered 16/7, 2023 at 18:15 Comment(0)
D
0

You can combine .filter() and .map() to achieve it.

let array = [];
for (let i = 1; i < 95; i++) array.push(i);
let limit = 10;
console.log(array.filter((item, index) => (index % limit === 0)).map((item, index) => {
    let tmp = [];
    for (let i = limit * index; i < Math.min((index + 1) * limit, array.length); i++) tmp.push(array[i]);
    return tmp;
}));
Daves answered 22/7, 2023 at 2:21 Comment(0)
L
0

This is what I just came up with. Might be a duplicate but I ain't reading through all these to check.

const toBatches = (src, batchSize) => src.reduce((pv, cv) => {
  const lastBatch = pv.length > 0 ? pv[pv.length - 1] : null;

  !lastBatch || lastBatch.length === batchSize 
    ? pv.push([cv])
    : lastBatch.push(cv); 

  return pv;
}, []);
Largehearted answered 7/8, 2023 at 3:12 Comment(0)
I
0

Since this is the first result I came across when trying to "pull" data from an array in chunks, I'll add this here.

When the integrity of the source array is not important, or when trying to "pull" data from the source in chunks, this simplified code can be used:

const chunkedQueue = [1, 2, 3, 4, 5, 6, 7, 8, 9];

while (chunkedQueue.length) {
    const chunk = chunkedQueue.splice(0, 10);

    // Do something with chunk
}

If using a stack approach, rather than a queue, a negative index can be used in the splice function. Though it is important to note, that the order of the items in each chunk is still the same as in the source array:

const chunk = chunkedStack.splice(-10, 10);
Imprecision answered 13/11, 2023 at 10:58 Comment(0)
M
-2

Well, a nice function for this would be:

function chunk(arr,times){
    if(times===null){var times = 10} //Fallback for users wanting to use the default of ten
   var tempArray = Array() //Array to be populated with chunks
    for(i=0;i<arr.length/times;i++){
     tempArray[i] = Array() //Sub-Arrays        //Repeats for each chunk         
   for(j=0;j<times;j++){
        if(!(arr[i*times+j]===undefined)){tempArray[i][j] = arr[i*times+j]//Populate Sub-  Arrays with chunks
    }
     else{
       j = times //Stop loop
       i = arr.length/times //Stop loop
  }
    }
     }
   return tempArray //Return the populated and chunked array
   }

Usage would be:

chunk(array,sizeOfChunks)

I commented it so you could understand what was going on.

(The formatting is a bit off, I programmed this on mobile)

Moye answered 12/8, 2016 at 14:32 Comment(0)
Z
-2

Neat and clean easy to understand

 let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 let len = nums.length ;

    const chunkArr = (arr, chunkNo) => {
      let newArr = [];
      for(let i = 0; i < len; i++){
        if(nums[0] !== "" && nums[0] !== undefined ){
          let a = nums.splice(0,chunkNo) ; 
          newArr.push(a);
        }
      }
       return newArr ;
    }
    console.log(chunkArr(nums, 5));
Zoller answered 9/4, 2020 at 19:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.