简体   繁体   中英

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.

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

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

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);

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM