0

Problem: Write a function that splits an array (first argument) into groups the length of size (second argument) and returns them as a two-dimensional array.

Why does my test2 variable not working?

function chunkArrayInGroups(arr, size) {
  let resArr = [];
  for (let i = 0; i < arr.length; i++) {
    resArr.push(arr.splice(0, size));
  }
  return resArr;
}
let test = chunkArrayInGroups(["a", "b", "c", "d"], 2);
console.log(test);
// returns correct [["a", "b"], ["c", "d"]]

let test2 = chunkArrayInGroups([0, 1, 2, 3, 4, 5], 2);
console.log(test2);
// should return [[0, 1], [2, 3], [4, 5]] 
//but returns [[0, 1], [2, 3]]

Why?

Thank you!

brk
  • 48,835
  • 10
  • 56
  • 78
Lesi
  • 11
  • 2
  • _Why?_ --> Because the array `arr` is being mutated/modified within the `for` loop. At each iteration, due to `.splice()`, the length of `arr` reduces by 2. It works in case-1 because the array you passed only has 4 elements. At iteration 1, array reduces to 2 elements & `i` is incremented from `0` to `1`. The check `i < arr.length` becomes `i < 2` and proceeds to next iteration. Now, at iteration 2, array reduces to 0 elements (in your first input - a, b, c, d; but in second input array still has 4 elements). So, for array with 4 elements it worked; but failed for next case. – jsN00b May 03 '22 at 15:26
  • In general, one tries to avoid modifying the array over which one is iterating, within the loop. – jsN00b May 03 '22 at 15:27

6 Answers6

1

arr.length changing on every iteration. And with incrementing i does not full fill condition.

Try below snippet

function chunkArrayInGroups(arr, size) {
  let resArr = [];

  while (arr.length) {
    resArr.push(arr.splice(0, size));
  }

  return resArr;
}

let test = chunkArrayInGroups(["a", "b", "c", "d"], 2);
console.log(test);

let test2 = chunkArrayInGroups([0, 1, 2, 3, 4, 5], 2);
console.log(test2);
MORÈ
  • 2,480
  • 3
  • 16
  • 23
1

Since we're using splice, we're modifying the length of the original array, which means we shouldn't rely on it to loop through. Instead, we should loop through a range equalling to the length of the array we want to return, which can be calculated by just dividing inputArr.length / size.

You can create a "range" and loop through it with a for..of loop by using Array(number).keys()

const caseOne = ['a', 'b', 'c', 'd'];
const caseTwo = [0, 1, 2, 3, 4, 5];
const caseThree = [1, 'hi', 3, 9, 'a', { hello: 'world' }, 7563, 'c', 3, [1, 2, 3]];

const chunkArray = (arr, num) => {
    // The final array we'll push to
    const final = [];

    // Loop through the future length of the "final" array
    for (const _ of Array(Math.ceil(arr.length / num)).keys()) {
        final.push(arr.splice(0, num));
    }

    return final;
};

console.log(chunkArray(caseOne, 2));
console.log(chunkArray(caseTwo, 2));
console.log(chunkArray(caseThree, 3));

You could also use the reduce method:

const caseOne = ['a', 'b', 'c', 'd'];
const caseTwo = [0, 1, 2, 3, 4, 5];
const caseThree = [1, 'hi', 3, 9, 'a', { hello: 'world' }, 7563, 'c', 3, [1, 2, 3]];

const chunkArray = (arr, num) => {
    return [...Array(Math.ceil(arr.length / num)).keys()].reduce((acc) => {
        acc.push(arr.splice(0, num));
        return acc;
    }, []);
};

console.log(chunkArray(caseOne, 2));
console.log(chunkArray(caseTwo, 2));
console.log(chunkArray(caseThree, 3));
mstephen19
  • 1,733
  • 1
  • 5
  • 20
1

You're using an index i which moves forward by one element, but meanwhile you're removing two per cycle, so the index falls beyond the array length sooner than you expect.

Instead of using an indexed for, just use a while condition that checks whether your array is empty or not. If it's not empty, countinue splice-ing:

function chunkArrayInGroups(arr, size) {


  let resArr = [];
  
  while (arr.length > 0) {
    resArr.push(arr.splice(0, size));
  }

  return resArr;
}

let test = chunkArrayInGroups(["a", "b", "c", "d"], 2); 
console.log(test);

let test2 = chunkArrayInGroups([0, 1, 2, 3, 4, 5], 2); 
console.log(test2);
GigiSan
  • 1,170
  • 2
  • 19
  • 30
1

function chunkArrayInGroups(arr, size) {
let resArr = [];
    
    for(i = 0; i < arr.length; i += size){
        resArr.push(arr.slice(i, i + size))
    }

    return resArr;
}

let test = chunkArrayInGroups(["f", "b", "c", "d", "d", "b"], 2);
console.log(test);
// returns correct [["a", "b"], ["c", "d"]]

let test2 = chunkArrayInGroups([0, 1, 2, 3, 4, 5], 3);
console.log(test2);

So this would be my method it uses slice instead of splice but this works like a charm.

TD3V
  • 301
  • 3
  • 12
0

Not only does arr.length change with every iteration, but the incrementor should be the size variable, rather than +1

function chunkArrayInGroups(arr, size) {
  let resArr = [], l = arr.length;
  for (let i = 0; i < l; i+=size) {
    resArr.push(arr.splice(0, size));
  }
  return resArr;
}

let test = chunkArrayInGroups(["a", "b", "c", "d"], 2);
console.log(test);
// returns correct [["a", "b"], ["c", "d"]]

let test2 = chunkArrayInGroups([0, 1, 2, 3, 4, 5], 2);
console.log(test2);
// should return [[0, 1], [2, 3], [4, 5]] 
//but returns [[0, 1], [2, 3]]
Kinglish
  • 23,358
  • 3
  • 22
  • 43
0

If you prefer a more functional approach

const chunkArrayInGroups = (arr, size) =>
  Array(Math.ceil(parseFloat(arr.length) / size))
  .fill(0)
  .map((_, i)=> arr.slice(i * size, (i + 1) *size))


let test = chunkArrayInGroups(["a", "b", "c", "d"], 2);
console.log(test);

let test2 = chunkArrayInGroups([0, 1, 2, 3, 4, 5], 2);
console.log(test2);
R4ncid
  • 6,944
  • 1
  • 4
  • 18