Search code examples
javascriptarraysspliceconways-game-of-life

Javascript splice and push in for loop adds items multiple times


I am attempting to learn Javascript by implementing Conway's Game of Life but unfortunately, I am stuck right at the beginning.

I have a matrix:

[
  [0,0,0,0], 
  [0,0,0,0], 
  [0,0,0,0], 
  [0,0,0,0]
] 

and would like to simulate a border of "1"s which surround the matrix. My final matrix should be:

[
  [1,1,1,1,1,1],
  [1,0,0,0,0,1], 
  [1,0,0,0,0,1],
  [1,0,0,0,0,1], 
  [1,0,0,0,0,1], 
  [1,1,1,1,1,1]
]

So far, I have written the following code:

var matrix = [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]];

function addBorder(m) {

  var row = []
  for (var i=0; i<m[0].length; i++) {
    row.push(0);
  }

  m.splice(0, 0, row);
  m.push(row);

  for (var i=0; i<m.length; i++) {
    m[i].splice(0,0,1);
    m[i].push(1);
  }

  return m;
}

addBorder(matrix).forEach(i => console.log(i.join(',')))

The output matrix is almost what I want, but not quite. However, if I change the second for loop to (var i=0; i<m.length; i++), the code produces the right matrix. Unfortunately, I have no idea why it does not work initially but with this altered line of code. Why does it add two items at the first and last loop? I am grateful for any hint!


Solution

  • The reason for the additional 1s in the first and last row is that they are the same row-object. You should avoid putting the same row object twice in your matrix.

    So change:

    m.splice(0, 0, row);
    m.push(row);
    

    to:

    m.splice(0, 0, row);
    m.push([...row]); // create a copy of row, and push that.
    

    Now when you perform your loop to pre- and postfix a 1, you'll be sure to target a different row object each time.

    Unrelated, but m.unshift(row) would be the same as m.splice(0, 0, row).

    In a more functional way, without mutating the original array, you could write the function like this:

    function addBorder(m) {
         return [
             Array(m.length+2).fill(1),
             ...m.map(row => [1, ...row, 1]),
             Array(m.length+2).fill(1)
         ];
    }
    
    var matrix = [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]];
    
    console.log(addBorder(matrix));

    Of course, you can still decide to assign the result to the original matrix variable:

    matrix = addBorder(matrix)