Use Recursion to Create a Countdown: Why is this not working?

Use Recursion to Create a Countdown: Why is this not working?
0

Tell us what’s happening:

Hi, I’m having trouble understanding why this doesn’t work, and I would really appreciate any help.
I tested the code in codepen.io and it works fine if I pass an empty array and any number as an argument, if I call the function like this:

countdown([], 5);

but if I call it using the name of the variable myArray, it doesn’t work, unless it is previously defined, like:

//This doesn’t work:
countdown(myArray, 5);
//This works:
var MyArray = [];
countdown(myArray, 5);

So I’m not sure if the variable myArray is supposed to be already defined in the example or not, especially because when I tested the proposed solution in codepen, it doesn’t work

This is the proposed solution:

function countdown(myArray, n){
  if(n <= 0){
    return;
  }
  else{
    myArray.push(n);
    countdown(myArray, n - 1);
  }
}

This is my code so far:


//Only change code below this line
function countdown(myArray, n) {
if (n < 1) {
  return [];
} else {
  var myArray = countdown(myArray, n -1);
  myArray.unshift(n);
  return myArray;
}
}

Thanks in advance!!

Your browser information:

User Agent is: Mozilla/5.0 (X11; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0.

Challenge: Use Recursion to Create a Countdown

Link to the challenge:
https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-javascript/use-recursion-to-create-a-countdown

So what you’re doing is, you’re confusing the global and local namespaces.

When we run script right in the console window, or we write script that executes without being in a function, we’re working in the context (or namespace) of the window object, the container javascript uses for everything. Any variable created is created somewhere within that space.

BUT, you won’t see them all in the window. Why not? Because that would very quickly get all clutter-y. Different developers and packages and frameworks and libraries would all likely crash, sharing variable names and stuff. Ugly and messy. Fortunately, while I say that all variables are in that space, some are nested. What do I mean by that?

If we write a function, let’s call it countdown() just for fun, there is something that happens when that function is executed. It creates its own little variable name ‘sandbox’. Its own namespace. The function countdown exists in the window namespace, sure - but any variables defined within countdown can’t be seen outside that sandbox.

It’s weird, because anything inside countdown can see all the namespaces of those containing elements, but not the other way around.

Why does this matter? Because, when you create your countdown function, like so:

function countdown(myArray, n){
  //
}

you have created a namespace, a private sandbox for your variables within that function, and created two new variables inside that sandbox! Yup. You created myArray and n in that local space. BUT!! Those are totally different from any variables of the same name outside that sandbox.

So, when you write countdown(myArray, 5);, you’re trying to run the function in the window or the global namespace, but you haven’t defined a variable myArray in the window’s collection of variables.

You don’t NEED to pass in a defined variable named myArray - it’s simply telling the javascript engine "hey, when you get inside my function, that first parameter? I want to refer to it locally by the name myArray.

So you could do

// Both of these do the same thing...
const emptyArrayOfEmptiness = [];
const anotherEmptyArray = new Array(0);

// While we call it emptyArrayOfEmptiness HERE, inside the function it's myArray
console.log(countdown(emptyArrayOfEmptiness, 10) );

// Same same
console.log(JSON.stringify(countdown(anotherEmptyArray, 25) ) );

// or even this - we pass in an empty array literal, and our myFullArray will hold
//  the return value from countdown which will be a FULL array.
const myFullArray = countdown([], 100);

But in each case, we aren’t referring to a global myArray, we are referring to the local variable of that name. So the name we use to pass the variable in? Doesn’t matter.

… and I apologize in advance for my wordiness. I don’t really tend to self-edit. :wink:

1 Like

Wow, wasn’t expecting such a thorough answer, thanks a lot!

So, it really shouldn’t matter what I name the variable inside the function, as long as the parameter I recieve is of the right type, correct?

What I still don’t get is why doesn’t my version work, but the proposed version does… they both name the variables “myArray” and “n”, and the instructions on the assignment says (this is one example):

After calling countdown(myArray, 5) , myArray should contain [5, 4, 3, 2, 1]

So… I’m supposed to pass those values to the function just like that, without defining myArray as a constant or as a variable outside of the function? If I copy and paste the proposed solution, it works, just like that… why??

I’m sorry, maybe I’m missing the point, but I just can’t wrap my head around it yet…

What you are not seeing is what is happening behind-the-scenes with the actual tests.

The tester has created another function called padArray which looks like the following:

function padArray(arr, n){
  countdown(arr, n);
  return arr;
}

For the specific test you reference above, the test calls padArray like so:

padArray([], 5); 

and compares the array returned from the above call to [5, 4, 3, 2, 1].

This challenge was meant to be simple for users, but because we were trying to hide some of the testing details, it has made it confusing for many of our users. We are in the process of rewriting the challenge which will make it much easier for users to test on their own.

2 Likes

Inside your function, you can call your parameters whatever you like. Here’s the outer bits of one I wrote that just passed:

//Only change code below this line
function countdown(chickenSoup, n){
  if(n<=0) /* do something at the end of the recursion... */;
  /* Modify that array we're calling chickenSoup 
   *  (which might be myArray outside this function, or not) */
  return countdown(chickenSoup, n-1);
}

As @RandellDawson says, the array being passed into your function is being set by the testing suite. You don’t see those. This, to my mind, is a good thing - your function needs to be able to operate in isolation. It doesn’t rely on anything outside of itself.

Now, here’s something ELSE to confuse the issue: javascript has two different classes of data types. Data primitives, like numbers or strings or booleans, and non-primitives, like objects or arrays.

Primitives are passed into functions by value, meaning you simply get the string or number or boolean or whatever. You don’t have access to the original variable’s space in memory, you simply get what value that memory position held.

Non-primitives, on the other hand, are passed into functions by reference. This means you don’t actually get the array or the object passed into your function. Javascript passes you a pointer to the place in memory where myArray is stored, and when you change it (even if you call it a different name within your function, like chickenSoup), it changes the original array or object.

So, within our function, even if we use a different name, we’re changing the original array that was passed in. We return the value, because it’s good form to return something, but when we return the completed array, we’re simply returning a reference back to that original position in memory!

1 Like

Thanks a lot to you both for taking the time to answer my doubts!
I still have a lot to keep learning, but I see it more clearly now, your answers were very helpful!

This is untrue, and I’m on a Quixotic crusade to stamp this nonsense out. Non-primitives are accessed through a reference, and that reference is passed by value. C# and PHP have actual pass-by-reference (and C++ has something we won’t talk about), which pertains to the scope the parameter is bound in, not the data type.

This pertains to Java, but it’s the same for Javascript and Python: https://www.journaldev.com/3884/java-is-pass-by-value-and-not-pass-by-reference