28

Please consider an array such as :

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

Is there a package that enable to do partitioning to obtain :

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

I can see how to do this with a for loop but would appreciate a "pre-made" function if existing.

13
  • 7
    Why do people prefer to call an external plugin function instead of writing 2 lines loop ? Commented Jul 5, 2012 at 13:28
  • 2
    Unfortunately, there is no pre-made function. Go with the for loop, but make it into a function too :) Commented Jul 5, 2012 at 13:28
  • 2
    @dystroy Because if you're already using the library anyway, a function call is clearer. Commented Jul 5, 2012 at 13:29
  • 2
    @dystroy: a) It's neater b) It may not even be a plugin; it could be built-in. For example, PHP's array_chunk. Commented Jul 5, 2012 at 13:29
  • 1
    @Pointy So it's not really like zip at all. Commented Jul 5, 2012 at 13:41

7 Answers 7

28

I think you will have to use a for loop, don't know of any inbuilt functions...

Try this function:

function splitarray(input, spacing)
{
    var output = [];

    for (var i = 0; i < input.length; i += spacing)
    {
        output[output.length] = input.slice(i, i + spacing);
    }

    return output;
}
Sign up to request clarification or add additional context in comments.

3 Comments

Using slice can avoid one loop.
Removed the extra for loop - Thank for pointing that out
This is just as simple as the accepted answer, and doesn't depend on any external libraries. +1
21

Here's a recursive solution:

function partition(array, n) {
  return array.length ? [array.splice(0, n)].concat(partition(array, n)) : [];
}    

This takes advantage of the fact that Array#splice destructively remove the specified items, and returns them as the function value. Note that this will destroy the input array, leaving it empty.

Comments

13

If using Underscore.js, you can implement this with groupBy() and values()

function partition(items, size) {
    var result = _.groupBy(items, function(item, i) {
        return Math.floor(i/size);
    });
    return _.values(result);
}

(This is less ugly in CoffeeScript.)

jsFiddle: http://jsfiddle.net/MW3BS/

2 Comments

Note that Underscore.js now has a partition function: underscorejs.org/#partition
That partition function only splits the array in two.
6

I've added this solution to @dystroy's jspref here and it appears to run twice as fast as the other solutions. Edit: in Safari & Chrome but not Firefox

Here is functional style solution to add to the mix of answers here.

It is a higher order function called toPartitions which returns a callback for underscore's reduce method or the native array reduce method.

Example usage:

[1,2,3,4,5,6,7,8,9].reduce( toPartitions( 3 ), [] );

The function:

function toPartitions ( size ) {
    var partition = [];
    return function ( acc, v ) {
        partition.push( v );
        if ( partition.length === size ) {
            acc.push( partition );
            partition = [];
        }
        return acc;
    };
}

Like Clojure's partition it will not include a tail partition when there are not enough elements.

In your example you could do:

arrayALLPartionned = arrayAll.reduce( toPartitions( 3 ), [] ) );

If you don't want to use this with reduce, but just have a function which takes an array and partition size you could do:

function partition ( arr, size ) {
    return arr.reduce( toPartitions( size ), [] );
}

Therefore the solution would just be:

arrayALLPartionned = partition( arrayAll, 3 );

2 Comments

This is a cool solution. My only qualm is that this won't effectively handle scenarios wherein the input array is not evenly divisible by the partition size. For example, calling partition(arrayAll, 4) will return two partitions while the last value, 9, is left absent. The fix for that would be inside the if conditional: if (partition.length === size || index === list.length - 1) { /* ... */ } Of course, a more bespoke solution then would be to perhaps include an additional parameter (e.g., withRemainder) to indicate if the user wants a remainder partition in the output.
Here is a fixed version: function toPartitions(size) { var partition = []; return function (acc, val) { partition.push(val); if (partition.length === 1) { acc.push(partition); } if (partition.length === size) { partition = []; } return acc; }; }
4

One more solution, with no external library :

function partition(items, size) {
    var p = [];
    for (var i=Math.floor(items.length/size); i-->0; ) {
        p[i]=items.slice(i*size, (i+1)*size);
    }
    return p;
}

Demonstration : http://jsfiddle.net/dystroy/xtHXZ/

5 Comments

thank you for your attention ! Would you mind explaining the use of "-->" ? I had never seen that syntax before.
That simply means "decrement i and compare the value after decrement to 0". It's just like doing i-- and comparing afterwards as i>0. The intersting point in looping that way is that you don't have to evaluate the maximal range value more than once as >0 can be computed in one operation. I don't say you really have to do it to make a fast code, it's not so important, but it's a habit I have.
excellent, i think it somehow correspond to my use of "reap and "sow" in Mathematica. thank you again.
This answer will truncate the results if it doesn't divide evenly into size
this solution will not consider the remainder: in your fiddle, you will miss the last element if you run it with 4 as size
2

You can write your own prototype method to do this

Array.prototype.partition = function(length) {
  var result = [];
  for(var i = 0; i < this.length; i++) {
    if(i % length === 0) result.push([]);
    result[result.length - 1].push(this[i]);
  }
  return result;
};

If you prefer not to add to the native prototype, you can write a simple function:

var partition = function(arr, length) {
  var result = [];
  for(var i = 0; i < arr.length; i++) {
    if(i % length === 0) result.push([]);
    result[result.length - 1].push(arr[i]);
  }
  return result;
};

You can see it in action on this jsFiddle demo.

1 Comment

Never modify the prototype of a native object, this can cause compatibility issues in the future.
1

Prototype has an array.partition function as well as an eachSlice() function. Sounds like eachSlice() is what you're looking for. If you're using jquery, there's a plug in to be able to use prototype functions. Here's a link to it... http://www.learningjquery.com/2009/02/implementing-prototypes-array-methods-in-jquery

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.