Search code examples
javascriptarrays

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!


Solution

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