23

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.

500
  • 6,509
  • 8
  • 46
  • 80
  • 7
    Why do people prefer to call an external plugin function instead of writing 2 lines loop ? – Denys Séguret Jul 05 '12 at 13:28
  • 2
    Unfortunately, there is no pre-made function. Go with the for loop, but make it into a function too :) – Ry- Jul 05 '12 at 13:28
  • 2
    @dystroy Because if you're already using the library anyway, a function call is clearer. – millimoose Jul 05 '12 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`. – Ry- Jul 05 '12 at 13:29
  • Well it's kind-of like the functional tool "zip" to combine elements from separate arrays, but different. I'm not sure why any library would provide something like this; it's pretty specialized. (*edit* oh well beaten again by php :-) – Pointy Jul 05 '12 at 13:30
  • Do you want to partition it by length or by total, by the way? – Ry- Jul 05 '12 at 13:31
  • Python doesn't have it either, does it? People asked for this often IIRC – Kos Jul 05 '12 at 13:32
  • @minitech, basically I am trying to figure out the means I have to replace this function in Mathematica : http://reference.wolfram.com/mathematica/ref/Partition.html . In this precise case by length. That is obtain subarray of size 3. – 500 Jul 05 '12 at 13:33
  • @Pointy So it's not really like zip at all. – millimoose Jul 05 '12 at 13:41
  • @millimoose yes ... it's more like "take n from" I guess, which I agree has general utility – Pointy Jul 05 '12 at 13:42
  • Just for fun I made a jsperf : http://jsperf.com/partition (I didn't check the other tested versions were correct). Don't take it too seriously though. – Denys Séguret Jul 05 '12 at 14:12
  • @dystroy I've updated your jspref with my answer. Just for fun too... not sure if my addition is a fair test! – Ross Nov 18 '14 at 14:54
  • @dystroy forgot the link sorry: http://jsperf.com/partition/3 – Ross Nov 18 '14 at 14:59

7 Answers7

23

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;
}
georg
  • 211,518
  • 52
  • 313
  • 390
starbeamrainbowlabs
  • 5,692
  • 8
  • 42
  • 73
19

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.

fat
  • 5,098
  • 4
  • 28
  • 31
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/

millimoose
  • 39,073
  • 9
  • 82
  • 134
7

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 );
Ross
  • 14,266
  • 12
  • 60
  • 91
  • 1
    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. – IsenrichO Dec 30 '17 at 02:57
  • 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; }; } – Kristian Kraljic Jul 24 '23 at 16:30
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/

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • thank you for your attention ! Would you mind explaining the use of "-->" ? I had never seen that syntax before. – 500 Jul 05 '12 at 19:15
  • 1
    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. – Denys Séguret Jul 05 '12 at 19:17
  • excellent, i think it somehow correspond to my use of "reap and "sow" in Mathematica. thank you again. – 500 Jul 05 '12 at 19:43
  • 3
    This answer will truncate the results if it doesn't divide evenly into `size` – Jay Sullivan Aug 14 '14 at 13:12
  • this solution will not consider the remainder: in your fiddle, you will miss the last element if you run it with 4 as size – tonnoz May 24 '20 at 15:09
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.

Peter Olson
  • 139,199
  • 49
  • 202
  • 242
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

Andy
  • 131
  • 4