0

I'm trying to split an array into chunks. The chunks should be as many as the function specifies. What I have already is

groupBySize = function(array, groupSize){
    if(groupSize === 0){
        return;
    }
    var groups = [];
    var i,j,temparray;
    for (i=0,j=array.length; i<j; i+=groupSize) {
        temparray = array.slice(i,i+groupSize);
        groups.push(temparray);
    }
    return groups;
};
groupByNumberOfGroups = function(array, NumberOfGroups){
    var groupSize = Math.floor(array.length/NumberOfGroups);
    var groups = this.groupBySize(array, groupSize);
    // Let's make sure we get the right amount of groups
    while(groups.length > NumberOfGroups){
        console.log(groups.length + ">" + NumberOfGroups);
        var last = groups[(groups.length-1)];
        for(var j = 0; j< last.length; j++){
            var temp = j;
            while(groups[temp].length > groups[temp+1]){
                temp++;
            }
            groups[j].push(last[j]);
        }
        groups.pop();
    }
    return groups;
};

This successfully splits the array up into the correct amount of chunks. I would like it to then make the length of each chunk as uniform as possible so if I were to split up an array like [1,2,3,4,5,6] into 4 chunks i would get [[1,2],[3,4],[5],[6]]. Any suggestions?

Another example of shortcomings: splitting up [1,2,3,4,5,6,7,8,9,10,11,12,13,14] into 8 chunks gives [[1,2,3,4,5,6,7],[8],[9],[10],[11],[12],[13],[14]]

Nordfjord
  • 162
  • 1
  • 7
  • I don't think so, the idea of that answer is already being used here, the OP asks about how to get the chunks proper size – Pablo Lozano May 13 '14 at 16:20

2 Answers2

4

Simple maths. If You have n items and want to split them into k chunks, every chunk will have n / k items. But what if n / k isn't an integer? Then some of the chunks will have n / k rounded down items and some will have n / k rounded down + 1 items. How many of them will have the + 1 items? n % k.

function distribute(a, k) { 

    var count = Math.floor(a.length / k); 
    var remain = a.length % k; 
    var result = []; 
    var index = 0; 

    for (var i = 0; i < k; i++) { 

        var number = count + (i < remain ? 1 : 0); 
        result.push(a.slice(index, index + number));
        index += number; 
    } 

    return result; 
}

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

--> [[1, 2, 3, 4], [5, 6, 7]]

Edit by Nordfjord: Shorter, but groups different values than the solution above:

groupByNumberOfGroups = function(array, numberOfGroups){
    var groups = []
    for(var i = 0; i < numberOfGroups; ++i) groups.push([]);
    for(var i = 0; i < array.length; ++i) groups[i%numberOfGroups].push(array[i]);
    return groups;
}
Roman Hocke
  • 4,137
  • 1
  • 20
  • 34
  • @Nordfjord: Hi, thanks for editing, but I guess Your code deserves it's own answer, instead of injecting mine. Your code gives different result than my function, so please create Your own answer with Your own code, so we can discuss it, thanks! – Roman Hocke May 13 '14 at 16:32
  • I don't have the rep to answer my question until after 8 hours :/ Will do that in the future though, thanks for your answer though, really helped. – Nordfjord May 13 '14 at 16:35
0

Try this,

var groupByNumberOfGroups = function( array, NumberOfGroups ){
    var div = Math.floor( array.length / NumberOfGroups );
    var mod = array.length % NumberOfGroups;
    var result = [];

    var lowerBound=0, upperBound=0;
    for ( var k=0; k<NumberOfGroups; k++ ) {
        lowerBound = ( upperBound === 0 ) ? ( ( k < mod ) ? k * ( div + 1 ) : k * ( div ) ) : upperBound
        upperBound = ( k < mod ) ? ( lowerBound + div + 1 ) : ( lowerBound + div )
        result[ k ] = array.slice( lowerBound, upperBound );
    }
    return result;
};

groupByNumberOfGroups([1,2,3,4,5,6], 4); // [[1, 2], [3, 4], [5], [6]]
groupByNumberOfGroups([1,2,3,4,5,6,7,8,9,10,11,12,13,14], 8); // [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13], [14]] 

js fiddle : link

Grainier
  • 1,634
  • 2
  • 17
  • 30