Trouble comprehending this Steamroller code

function steamrollArray(arr) {

  var myArr = [];
  
  for (var i=0; i<arr.length; i++) {
  
    if (!Array.isArray(arr[i])) {
      myArr.push(arr[i]);
  }

    else {
      var subArray = steamrollArray(arr[i]);
      myArr = myArr.concat(subArray);
  } 
}
  return myArr; 
}  
steamrollArray([1,[2],[[3]]]);

Just trying to make sense of what is going on here.

-The first number in the array is easy; that gets pushed right away.

-The second number is an array. Ok, so we call the function again with this value as it’s argument. Because it’s only one level deeper it should be pushed on that iteration.

-What I really don’t get is how line 13 comes into play. I would think that since we are calling the function again for [2] that it would be pushed to myArray by the first if statement… if this were true than line 13 would be redundant. Obviously it’s vitally necessary, could someone talk me through why? (Preferably really slowly, recursion makes my head hurt!)

Yes, you’re right, since 2 is not an array, it gets pushed to myArr.But which one ?

Each time steamrollArray (or any function, actually) is called, a new context is created.Think of it as creating a new tiny universe.

So this is how the story begins :

At line 20, when you call steamrollArray([1,[2],[[3]]]), you create a universe.Let’s call it universe A.

Within this universe A, you are using the var keyword on myArr, which means you’re creating a new variable called myArr, and this variable belongs to universe A.

then at line 13, you call steamrollArray again.You’re creating a new tiny universe, inside universe A! Let’s call it universe B.

Within universe B, because the keyword var is used on myArr, you’re also creating a new variable, that belongs to universe B, and is completely different from the myArr from universe A!

The loop and if statement within universe B will push 2 to myArr (the one from universe B), and in the end, universe B returns myArr (which is [2] ), and just vanishes.No more universe B. The value [2] is then sent to universe A subArray variable.

You already know the end of the story. myArr from universe A will concatenate [2], giving [1,2].And after that, the same thing will happen with [[3]], you will call steamrollArray again, create universe C, then universe D inside universe C,…

One important thing to understand here is, if you use the var keyword within a function, you are creating a new variable for this function.In this case, with the use of recursion, you’re not overriding other variables by using the same variable name, because you’re not declaring them in the same context.

One other thing to understand on the subject is (see my example below), within universe B(a nested function) you could have access to universe A(the wrapping context) variables.The universe A scope is available to universe B, because universe B is inside universe A.

One last thing: while you could access universe A scope from inside universe B, universe A cannot have access to universe B variables.

var name = "jack";
var name = "mike";

console.log(name); // mike

function someFunction() {
  var name = "bill";
  var billAge = " 'bill is 20' ";
  function nestedFunction() {
    var name = "tom";
    var gender = "male";
    console.log(name, billAge);
  }
  
  nestedFunction(); // tom 'bill is 20'
  console.log(name); // bill
  console.log(gender); // ReferenceError: gender is not defined
  
}

someFunction();

update :

Here’s another example to help you :

function someFunction() {
  
  var name = "sam";
  
  function anotherFunction() {
    var name = "john";
    yetAnotherFunction(); // sam
  }
  
  function yetAnotherFunction() {
    console.log(name);
  }
  
  anotherFunction();
  
}

someFunction();
3 Likes

This is an amazingly detailed and well thought out answer. Your analogy was perfect, and now what’s going on in the multiple function calls makes a lot more sense to me. Thank you so much! :smiley: