How do I create a collection of arrays from a sing

2019-06-14 18:21发布

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.

4条回答
淡お忘
2楼-- · 2019-06-14 18:36

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.

查看更多
叼着烟拽天下
3楼-- · 2019-06-14 18:37

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

查看更多
smile是对你的礼貌
4楼-- · 2019-06-14 18:58

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);
查看更多
Ridiculous、
5楼-- · 2019-06-14 19:01

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]] ]
查看更多
登录 后发表回答