Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Splitting a JS array into N arrays

Imagine I have an JS array like this:

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 

What I want is to split that array into N smaller arrays. For instance:

split_list_in_n(a, 2) [[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11]]  For N = 3: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]  For N = 4: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11]]  For N = 5: [[1, 2, 3], [4, 5], [6, 7], [8, 9], [10, 11]] 

For Python, I have this:

def split_list_in_n(l, cols):     """ Split up a list in n lists evenly size chuncks """     start = 0     for i in xrange(cols):         stop = start + len(l[i::cols])         yield l[start:stop]         start = stop 

For JS, the best right solution that I could come up with is a recursive function, but I don't like it because it's complicated and ugly. This inner function returns an array like this [1, 2, 3, null, 4, 5, 6, null, 7, 8], and then I have to loop it again and split it manually. (My first attempt was returning this: [1, 2, 3, [4, 5, 6, [7, 8, 9]]], and I decided to do it with the null separator).

function split(array, cols) {     if (cols==1) return array;     var size = Math.ceil(array.length / cols);     return array.slice(0, size).concat([null]).concat(split(array.slice(size), cols-1)); } 

Here's a jsfiddle of that: http://jsfiddle.net/uduhH/

How would you do that? Thanks!

like image 701
Tiago Avatar asked Nov 18 '11 20:11

Tiago


2 Answers

You can make the slices "balanced" (subarrays' lengths differ as less as possible) or "even" (all subarrays but the last have the same length):

function chunkify(a, n, balanced) {            if (n < 2)          return [a];        var len = a.length,              out = [],              i = 0,              size;        if (len % n === 0) {          size = Math.floor(len / n);          while (i < len) {              out.push(a.slice(i, i += size));          }      }        else if (balanced) {          while (i < len) {              size = Math.ceil((len - i) / n--);              out.push(a.slice(i, i += size));          }      }        else {            n--;          size = Math.floor(len / n);          if (len % size === 0)              size--;          while (i < size * n) {              out.push(a.slice(i, i += size));          }          out.push(a.slice(size * n));        }        return out;  }      ///////////////////////    onload = function () {      function $(x) {          return document.getElementById(x);      }        function calc() {          var s = +$('s').value, a = [];          while (s--)              a.unshift(s);          var n = +$('n').value;          $('b').textContent = JSON.stringify(chunkify(a, n, true))          $('e').textContent = JSON.stringify(chunkify(a, n, false))      }        $('s').addEventListener('input', calc);      $('n').addEventListener('input', calc);      calc();  }
<p>slice <input type="number" value="20" id="s"> items into  <input type="number" value="6" id="n"> chunks:</p>  <pre id="b"></pre>  <pre id="e"></pre>
like image 174
georg Avatar answered Oct 18 '22 11:10

georg


I think this way using splice is the cleanest:

splitToChunks(array, parts) {     let result = [];     for (let i = parts; i > 0; i--) {         result.push(array.splice(0, Math.ceil(array.length / i)));     }     return result; } 

For example, for parts = 3, you would take 1/3, then 1/2 of the remaining part, then the rest of the array. Math.ceil ensures that in case of uneven number of elements they will go to the earliest chunks.

(Note: this destroys the initial array.)

like image 33
Senthe Avatar answered Oct 18 '22 09:10

Senthe