9

I have the array: [1,2,3,4,5,6,7]

I want to achieve: [[1,2], [3,4], [5,6], [7]]

I'm thinking Array.map, but it doesn't seem to be able to map to nothing for an element?

I have (using Underscorejs):

arr.map(function(el, idx, arr) {
  if (idx%2 != 0) return null;
  if (idx == arr.length-1) return [el];
  return [el, arr[idx+1]]
}).compact();

This is still a bit ugly. How can I achieve the transformation (without explicit loop)?

Boyang
  • 2,520
  • 5
  • 31
  • 49
  • Can you share what have you tried so far? Also you cannot avoid loops. `Array.map` will create a loop as well. Its just more cleaner approach. – Rajesh Mar 16 '16 at 12:17

6 Answers6

6

The map function can't do that, it's a structure-preserving transformation.

You could write this "chunking" as a reduce, a plain loop, or something weird like

var arr = [1,2,3,4,5,6,7];
return arr.filter(function(_, i) {
    return i%2==0;
}).map(function(x, i) {
    return i*2+1<arr.length ? [x, arr[i*2+1]] : [x];
});

or

return arr.map(function(_, i) {
    return arr.slice(i, 2);
}).filter(function(_, i) {
    return i%2==0;
})

See also Split array into chunks for many more variants (some of them quite functional).

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
6

reduce the array using the modulo operator:

function chunk(arr, n) {
  return arr.reduce(function (p, c, i) {
    if (i % n === 0) p.push([]);
    p[p.length - 1].push(c);
    return p;
  }, []);
}

chunk(arr, 2); // [[1,2],[3,4],[5,6],[7]]

DEMO

Andy
  • 61,948
  • 13
  • 68
  • 95
4

One more wired solution in one loop.

var array = [1, 2, 3, 4, 5, 6, 7],
    result = array.reduce(function (r, a, i) {
        i & 1 && r[i >> 1].push(a) || r.push([a]);
        return r;
    }, []);

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

Or functional (it utilized Fat_Freddy's idea)

function due(array) {
    return array.length && [array.splice(0, 2)].concat(due(array)) || [];
}

var array = [1, 2, 3, 4, 5, 6, 7],
    result = due(array.slice());

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Community
  • 1
  • 1
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Great answer specially `due()`, but it will change original array. I'd suggest using `array.slice()` instead, but then I guess recursion will not work. – Rajesh Mar 16 '16 at 12:44
3

Try a combination of map and filter

var output = [1,2,3,4,5,6,7].map(function(value,index,arr){
   if(index%2==0)
   {
     //return [value, arr[index+1]];
     return index + 1 < arr.length ? [value, arr[index+1] ] : [ value ];
   }
   else
   { 
     return false;
   }
}).filter(function(value){return value});
console.log(output);
gurvinder372
  • 66,980
  • 10
  • 72
  • 94
3

Can't you just do something like this?

EDITED to prevent original being altered, as per Andy's suggestion

var temp = original.slice();
var newList = [];
while(temp.length)
{
    newList.push(temp.splice(0,2));
}
Toby
  • 1,537
  • 10
  • 19
  • 2
    This might be better it didn't destroy the original array. Perhaps clone it first with `var array2 = array.slice(0);`. – Andy Mar 16 '16 at 12:36
  • 1) I didn't downvote you. 2) I was just making a suggestion. – Andy Mar 16 '16 at 12:43
1

You can try array.slice() and while loop for it.

Also using function like map, filter will iterate every element.

function splitArrayInLength(arr, len) {
  var returnArr = [];
  var index = 0;
  while(index < arr.length){
    returnArr.push(arr.slice(index, index + len));
    index += len;
  }
  return returnArr;
}

function main() {
  var arr = [1, 2, 3, 4, 5, 6, 7];
  print(splitArrayInLength(arr, 2));
  print(splitArrayInLength(arr, 4));
}

function print(obj){
  document.write("<pre>" + JSON.stringify(obj, 0, 4) + "</pre><br/>");
}

main();
Rajesh
  • 24,354
  • 5
  • 48
  • 79