Reputation: 19251
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
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
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
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
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
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