Andy
Andy

Reputation: 19251

How do I create a collection of arrays from a single array?

Say I have one large array like

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

and would like to split it into an array of n-tuples like

[[1,2], [3,4], [5,6], [7,8], [9,10], [11,12], [13,14] /*, ... */ ] // (for n=2)

Is there some easy way to achieve this? The special case n = 2 would be enough for me.

Upvotes: 2

Views: 1213

Answers (5)

Sadegh Ghafari Yazdi
Sadegh Ghafari Yazdi

Reputation: 99

You can do this using reduce and Modulus (%):

const grouper = (array, n) => array.reduce((a, b, i) => {
        i % n == 0 ?
            a.push([b])
            :
            a[a.length - 1].push(b);
        return a;
    }, []);

let result = grouper([1,2,3,4,5,6,7,8],2);    

console.log(result);
//[[1,2],[3,4],[5,6],[7,8]]

you can read about JavaScript reduce here.

Upvotes: 0

georg
georg

Reputation: 214949

In python this can be done with zip(*[iter(xs)]*n). Just for fun, here's a JS implementation:

Let's start with a poor man's generator (that's all we've got until ES6 spreads around):

StopIteration = {"name": "StopIteration"}

function iter(xs) {
    if('next' in xs)
        return xs;
    var i = 0;
    return {
        next: function() {
            if(i >= xs.length)
                throw StopIteration;
            return xs[i++];
        }
    }
}

next = function(it) { return it.next() }

zip() is trivial:

zip = function() {
    var args = [].map.call(arguments, iter), chunks = [];
    while(1) {
        try {
            chunks.push(args.map(next));
        } catch(StopIteration) {
            return chunks;
        }
    }
}

Now, to create chained pairs just pass the same iter twice to zip:

xs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

it = iter(xs)
a = zip(it, it)

console.log(a)
// [[1,2],[3,4],[5,6],[7,8],[9,10],[11,12]]

For N-pairs an additional utility is required:

repeat = function(x, n) {
    for(var a = []; n; n--)
        a.push(x);
    return a;
}

a = zip.apply(this, repeat(iter(xs), 5))

console.log(a) 
// [[1,2,3,4,5],[6,7,8,9,10]]

Note that like in Python this strips incomplete chunks.

Upvotes: 1

elclanrs
elclanrs

Reputation: 94101

This should work:

for (var i=0; i<arr.length; i+=2) {
  result.push([arr[i], arr[i+1]]);
}

Came up with this, it should work for any number of "pockets" or whatever you want to call them. It checks for undefined so it works with odd number of items:

Array.prototype.pockets = function(n) {

  var result = [],
      pocket = [],
      i, j;

  for (i=0; i<this.length; i+=n) {
    pocket.length = 0;
    for (j=1; j<n; j++) if (this[i+j] != null) pocket.push(this[i+j]);
    result.push([this[i]].concat(pocket));
  }

  if (arguments.length > 1) {
    return result.pockets.apply(result, [].slice.call(arguments,1));
  }

  return result;
};

// Usage:
var arr = [1,2,3,4,5,6,7,8,9,10,11];

arr.pockets(2); //=> [[1,2],[3,4],[5,6],[7,8],[9,10],[11]]
arr.pockets(3); //=> [[1,2,3],[4,5,6],[7,8,9],[10,11]]

// Recursive:
arr.pockets(1,3); //=> [ [[1],[2],[3]], [[4],[5],[6]], [[7],[8],[9]], [[10],[11]] ]

Upvotes: 6

Jonathan Lonowski
Jonathan Lonowski

Reputation: 123433

For an underscore variant, you can achieve this with _.groupBy(), grouping by the index of the item:

var doubles = _.groupBy(singles, function (num, i) {
    return Math.floor(i / 2);
});

Though, since _.groupBy() returns an Object, getting an Array takes some additional work:

_.mixin({
    segment: function (coll, per) {
        var result = [];
        _.chain(coll)
            .groupBy(function (item, i) { return Math.floor(i / per)})
            .each(function (group, key) { result[key] = group; })
        return result;
    }
});

var singles = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];

var doubles = _.segment(singles, 2);
var triples = _.segment(singles, 3);

Upvotes: 3

lqc
lqc

Reputation: 7328

This can be done much simpler by using Array.slice:

function grouper(lst, size) {
    var result = [], i=0, n=lst.length;
    while(i < n) {
        result.push(lst.slice(i, i+size));
        i += size;
    }
    return result
}

It's also much more efficient: http://jsperf.com/grouper

Upvotes: 6

Related Questions