Caution When Reinitializing Variables Inside a Loop

Caution When Reinitializing Variables Inside a Loop
0

#1

Tell us what’s happening:
If I don’t re-initialized “row = []” inside forLoop then
matrix = (3) [Array(6), Array(6), Array(6)].

I don’t understand how each array at each index has 6 elements in it because according to me it should be
matrix = (3) [Array(2), Array(4), Array(6)].

Correct me where I’m going wrong?

Your code so far


function zeroArray(m, n) {
  // Creates a 2-D array with m rows and n columns of zeroes
  let newArray = [];
  let row = [];
  for (let i = 0; i < m; i++) {
    // Adds the m-th row into newArray
    
    for (let j = 0; j < n; j++) {
      // Pushes n zeroes into the current row to create the columns
      row.push(0);
    }
    // Pushes the current row, which now has n zeroes in it, to the array
    newArray.push(row);
  }
  return newArray;
}

let matrix = zeroArray(3, 2);
console.log(matrix);

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36.

Link to the challenge:
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/debugging/use-caution-when-reinitializing-variables-inside-a-loop


#2

This is a really good question and really well spotted.

You’re quite correct, according to the code it should be matrix = (3) [Array(2), Array(4), Array(6)].

I can’t tell you for sure what’s happening but I can tell you what it looks like - that push() isn’t just adding the numbers to newArray, it seems to be merging them in some way. Any changes you make to row seem to be reflected in newArray. This seems to be true even when row is defined in the for loop.

However, the two variables don’t seem to point at the same object (== comes back negative).

I think an afternoon with You Don’t Know Javascript may be called for.


#3

The answer lies in reference types, newArray is getting a reference to row pushed to it, when row updates it data it is reflected inside newArray because it’s data is just a pointer to row. The two arrays are not the same object, but the data inside newArray is a reference pointer to the row object. The loops has nothing to do with it.

const row = [0, 0];
const newArray = [];
newArray.push(row);
row.push(0,0)
newArray // is now [0, 0, 0, 0]
row.push('pass', 'by', 'reference')
newArray // is now [0, 0, 0, 0, "pass", "by", "reference"]

#4

You need to reinitialize the row variable to output the expected results because inside both loops you are using a reference in memory where Javascript stores the row data.

Let me explain it by using your code:

The first time you push the row into newArray in the first “round”, you’re pushing a reference to the array, not just the values; in this case row is [0,0]. That’s fine so far. However, when you do this the second time, you would expect it to be pushing [0,0,0,0] at the end of newArray, leaving its value like this: [[0,0],[0,0,0,0]], right? Wrong. Since you are using references, any time you change the length or values of the row array, you are also applying these changes to any variables referencing this array, like newArray (again, because it’s using references instead of values). So, when you push the row array into newArray the second time, the latter becomes [[0,0,0,0][0,0,0,0]], and [[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]] the last time you do it.

How to solve this? Well, as you mentioned, you can reinitialize the variables inside the outer loop and it will work fine. You can also push a copy of row instead of the reference. Using the spread operator is quite straightforward, as you can see in the following modified snippet:

function zeroArray(m, n) {
  let newArray = [];
  let row = [];
  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      row.push(Math.random());
    }
    newArray.push([...row]); // [...row] <-- Use of spread operator.
  }
  return newArray;
}

let matrix = zeroArray(3, 2);
console.log(matrix);

Hope it helps