The problem is that you do not pass on the array to the recursive call, so each recursive execution creates a new, empty array. As a consequence, it does not return the array that had a value pushed to it, but the new, empty one that is coming back from the recursive calls.
Secondly, you never push value 1 to the array. So it would be better to stop the recursion at 0 instead of 1.
So taking those two fixes, you get this:
function countdown(n, newArr=[]) {
if (n <= 0) {
return newArr;
}
newArr.push(n);
return countdown(n - 1, newArr)
}
console.log(countdown(5));
Your alternative solution is clean, because it does not need to pass an array as argument. It uses the returned array to add the next value to it (in front of it). It would have my preference.
To understand how it works, print out the intermediate values:
function countdown(n) {
if (n < 1) {
console.log("At the end of recursion. Creating and returning an empty array.");
return [];
} else {
const arr = countdown(n - 1);
console.log("Got the following array back from the recursive call:");
console.log(JSON.stringify(arr));
arr.unshift(n);
console.log("Prefixing it with " + n + " and returning the result:");
console.log(JSON.stringify(arr));
return arr;
}
}
var result = countdown(5);
Answer from trincot on Stack OverflowThe problem is that you do not pass on the array to the recursive call, so each recursive execution creates a new, empty array. As a consequence, it does not return the array that had a value pushed to it, but the new, empty one that is coming back from the recursive calls.
Secondly, you never push value 1 to the array. So it would be better to stop the recursion at 0 instead of 1.
So taking those two fixes, you get this:
function countdown(n, newArr=[]) {
if (n <= 0) {
return newArr;
}
newArr.push(n);
return countdown(n - 1, newArr)
}
console.log(countdown(5));
Your alternative solution is clean, because it does not need to pass an array as argument. It uses the returned array to add the next value to it (in front of it). It would have my preference.
To understand how it works, print out the intermediate values:
function countdown(n) {
if (n < 1) {
console.log("At the end of recursion. Creating and returning an empty array.");
return [];
} else {
const arr = countdown(n - 1);
console.log("Got the following array back from the recursive call:");
console.log(JSON.stringify(arr));
arr.unshift(n);
console.log("Prefixing it with " + n + " and returning the result:");
console.log(JSON.stringify(arr));
return arr;
}
}
var result = countdown(5);
You need to hand over the result array for the recursive call. And you need to check if no value is left, ten return the result array.
function countdown(n, result = []) {
if (n < 1) return result;
result.push(n);
return countdown(n - 1, result);
}
console.log(countdown(5));
As another approach, you could return an array and for the exit condition take the final value, otherwise take n and the spreaded result of the recursive call.
function countdown(n) {
if (n < 1) return [];
return [n, ...countdown(n - 1)];
}
console.log(countdown(5));
Basic JavaScript: Use Recursion to Create a Countdown
javascript - Recursive countdown timer - Stack Overflow
Basic JavaScript: Use Recursion to Create a Countdown
That if clause is checking whether the argument is less than 1. If you’ve passed in 5, that’s more than 1 so the if block is never executed.
This is the recursion part. The thing to think about is that the code block will recur until the if clause is met, which returns an empty array. So the first line of the else cause is what will execute recursively. This returns an empty array which then has all of the numbers pushed into it as the blocks of code from each prior recursion get executed.
I’m not a huge fan of this sort of recursive pattern because it values brevity over clarity. In most real world circumstances, the marginal reduction in computational overhead will be of no benefit if others working on the code have a hard time understanding it.
More on reddit.comjavascript - How does recursion work in a Countdown function - Stack Overflow
Videos
Hi guys, I am stuck on a very simple recursion problem where I am asked to use recursion to count down from n and log each number. The prompt is as follows:
"Write a recursive function that accepts a positive integer n as an input and logs every number from n (inclusive) to 0 (exclusive) to the console"
And here is my attempt:
function countDown (n) {
//base case
if (n <= 0) { return 0}
// recursive call
else { countDown (n-1)
console.log(n)
}
Trying to figure out where I am going wrong here and I appreciate any feedback. Thanks!!!
You need to define current outside of your function. Currently you are resetting it to 1000 every time the function is run.
here you go:
var target = 1500000; // 25 mins
var current = 0; // 0 secs
function countdown() {
current += 1000;
var diff = target-current; // calculates the 25 minutes
var min = Math.floor(diff/1000/60); //gets mins
var sec = (diff/1000) % 60; // gets secs
document.getElementById("txt").innerHTML = min + ":" + sec;
if (diff > 0)
setTimeout(countdown, 1000);
}
countdown();
JSFiddle with running example: https://jsfiddle.net/epcmw0uc/5/
hello everyone,I'm having a hard time understanding how the code from this exercise works, i tried using debugger but I got more confused
function countup(n) {if (n < 1) {return [];} else {const countArray = countup(n - 1);countArray.push(n);return countArray;}}console.log(countup(5)); // [ 1, 2, 3, 4, 5 ]
what I understand so far is that the function countup(5) gets executed then the recursion keeps happening till n=0
if (n < 1) {return [];}
now we have an empty array and that's all I understand😅
1.why am I not kicked out of the if statement after that, how does the function keeps executing??
2.the function then start pushing the numbers incrementally, what causes the increment here?? Also when I the debugger I noticed that the function would hit the last curly bracket and then jump back tocountArray.push(n); how does that happen .. any clarification would be appreciated
That if clause is checking whether the argument is less than 1. If you’ve passed in 5, that’s more than 1 so the if block is never executed.
This is the recursion part. The thing to think about is that the code block will recur until the if clause is met, which returns an empty array. So the first line of the else cause is what will execute recursively. This returns an empty array which then has all of the numbers pushed into it as the blocks of code from each prior recursion get executed.
I’m not a huge fan of this sort of recursive pattern because it values brevity over clarity. In most real world circumstances, the marginal reduction in computational overhead will be of no benefit if others working on the code have a hard time understanding it.
the return happens when n<1.
As far as how the recursion works, it happens on the first line of the else block. You create a variable and assign it the value of executing countup with (n-1). You can't go to the next line until this line has resolved.
So what happens? you run another instance of countup, this time with n-1. If that n is now less than 1, it'll return the empty array, and you don't hit the else block, meaning you don't execute countup again.
At that point the countup inside of your countup returns []. You do get kicked out of the if statement, but it's the inside countup, meaning there's still the outside countup, where you were figuring out what countarray is. Now that we know that countArray is [], we can push n to that array and return it.
Realise that every execution of countup will have its own n and countArray variables.
It may help to visualise it. Each execution context is visualised as a "box". The variables in the outer boxes will still be there, when a function call returns.
The outermost box is the execution context that is created by the initial call: countup(5):
// n is 5 and does not change
const countArray = countup(n - 1);
+-------------------------------------------------------------------------------+
| // n is 4 and does not change |
| const countArray = countup(n - 1); |
| +-------------------------------------------------------------------------+ |
| | // n is 3 and does not change | |
| | const countArray = countup(n - 1); | |
| | +-------------------------------------------------------------------+ | |
| | | // n is 2 and does not change | | |
| | | const countArray = countup(n - 1); | | |
| | | +-------------------------------------------------------------+ | | |
| | | | // n is 1 and does not change | | | |
| | | | const countArray = countup(n - 1); | | | |
| | | | +-------------------------------------------------------+ | | | |
| | | | | // n is 0 and does not change | | | | |
| | | | | return []; // the if-block is executed because n < 1 | | | | |
| | | | +-------------------------------------------------------+ | | | |
| | | | // countArray is [] | | | |
| | | | countArray.push(n); // n is still 1 | | | |
| | | | return countArray; // returns [1] | | | |
| | | +-------------------------------------------------------------+ | | |
| | | // countArray is [1] | | |
| | | countArray.push(n); // n is still 2 | | |
| | | return countArray; // returns [1, 2] | | |
| | +-------------------------------------------------------------------+ | |
| | // countArray is [1, 2] | |
| | countArray.push(n); // n is still 3 | |
| | return countArray; // returns [1, 2, 3] | |
| +-------------------------------------------------------------------------+ |
| // countArray is [1, 2, 3] |
| countArray.push(n); // n is still 4 |
| return countArray; // returns [1, 2, 3, 4] |
+-------------------------------------------------------------------------------+
// countArray is [1, 2, 3, 4]
countArray.push(n); // n is still 5
return countArray; // returns [1, 2, 3, 4, 5]
You can just add some logging to visualize what is happening:
function countup(n) {
if (n < 1) {
console.log('n = %d, returning empty array', n);
return [];
} else {
console.log('n = %d, calling countup(%d - 1)', n, n);
const countArray = countup(n - 1);
console.log('n = %d, countArray is %s', n, JSON.stringify(countArray))
console.log('n = %d, pushing n onto array', n);
countArray.push(n);
console.log('n = %d, returning %s', n, JSON.stringify(countArray));
return countArray;
}
}
console.log(countup(5));
Why is the array declared as a constant and assigned to the recursion call?
It is not. The array is variable. Every call to this function will return a new array. No arrays get "overwritten". const in JavaScript is merely used to declare a constant variable which has lexical scope (that is, the variable arr ceases to exist after return arr) and is constant (can't be mutated / assigned to).
I get the unshifting of the current value, but why is it declared after the recursive call? Shouldn't it be positioned before (one line above) so as to unshift the value before the function loops itself?
You can't position it above since the recursive call is required to obtain arr in the first place. After you have counted down from n-1 to 1, you must prepend n at the first position, which is what unshift does.
Similarly, why is the full final array returned within the else loop? Shouldn't it be better returned outside, or on the base condition above, so as to not be returned every time the function loops?
There is no such thing as an "else loop". The reason for returning the array there as well is that you want countdown(n) to return the array it has just generated, using the array countdown(n-1).
As it is, my understanding is that when n reaches 0, the loop should return an empty array, and not the full array that has been created with the recursion.
That's exactly what if (n < 1) return []; does.
That said, for the task at hand the recursive implementation given is highly suboptimal. First of all, you don't need the else since return will exit the function anyways:
function countdown(n) {
if (n < 1) return [];
const arr = countdown(n - 1);
arr.unshift(n);
return arr;
}
second, this is most readable (and performant) as a simple for loop:
function countdown(n) {
const arr = Array(n)
for (let i = 0; i < n; i++) arr[i] = n - i;
return arr;
}
(bonus: the array size being known ahead of time can be leveraged using the Array constructor to prevent JS having to resize the Array as you push elements).
third, the current implementation has a quadratic time complexity of O(n²) because arr.unshift is a linear-time operation which is applied n times to arrays of length 0 to n-1.
Recursion is extremely useful when you need the stack (f.E. in a depth-first traversal). Creating a countdown doesn't require a stack. This is not using but rather abusing recursion to implement a highly suboptimal algorithm. If you - for whatever reason (perhaps you are forced to use a purely functional language that has no loops, only recursion?) - wanted to replace the perfectly fine for loop with a recursive call at all cost, simply use tail recursion and lexical scope (closures):
function countdown(n) {
const arr = [];
function loop(n) {
if (n < 1) return;
arr.push(n);
loop(n-1);
}
loop(n);
return arr;
}
or if you don't like closures, what about an optional arr parameter? This allows turning the recursion around since the caller now has the arr before the callee and can thus append its number before subsequent recursive calls do the same:
function countdown(n, arr = []) {
if (n < 1) return arr;
arr.push(n);
countdown(n - 1, arr);
return arr;
}
same result, but (IMO) cleaner, and most importantly, significantly faster code. Try running all these functions on n = 1e6 and you will see that the freeCodeCamp one fails to complete in a reasonable timeframe whereas the other ones finish almost instantly.
It might help to clarify how the code is working a little. Hopefully, the explanation clears up your questions a bit. Let's start with the most basic case:
countdown(0) - When you call countdown(0) your if-block runs and returns an empty array []. So we see that countdown(0) outputs []. This is the base case.
Next, let's imagine we call the function again, this time as:
countdown(1) - In this scenario the else block runs. We see that it calls countdown(0), which means the interpreter executes the function again, passing n as 0. Above we saw what happens when countdown(0) is called, it returns an empty array [], so that's what gets assigned to arr within the else block. We then unshift n (which is 1 here) onto this empty array, which adds 1 to the start of the array stored in arr. This is then returned. So we say that countdown(1) outputs [1].
Now let's see what happens if we were to call the function again with 2:
countdown(2) - Again, the else block runs, triggering a call to countdown(1). We saw above what occurs when this is called, we get back an array with [1], which is stored in the local variable arr, and we then .unshift(2) so that 2 is added to the beginning of the array which we then return. So we see that countdown(2) returns [2, 1].
When thinking about recursion, it can be helpful to think of it as breaking down your input until it's at a small enough value that it can't be broken down anymore (your base case). Once you hit that, you work your way back up, stitching together your previous calls now that you have "solved" the sub-problems.
To answer your questions more directly:
arris assigned to the recursive call so that we can build upon the results it returned. Ifcountdown(1)returns[1], then we can build onto that result by storing it inarrand unshift2onto it to calculatecountdown(2). It could have been madelet, butconstis commonly used if the variable doesn't need to be reassigned. We don't want to movearroutside of the function as then callingcountdown()multiple times will modify the same global array. You would also need to change your code so that we can gradually "build-up" the result by further recursive calls if you did that also, which is more of a "loop mindset" than a "recursive mindset".Moving the
.unshift()component before the recursive call wouldn't work as you wouldn't have anything to unshift to. The recursive call is what provides you with the array result ofcountdown(n-1)so that you can build onto that later by using.unshift().Returning your array in the
elseblock is what allows you to obtain results whennis greater than0. In thecountdown(1)example above, we need toreturn arrso that it outputs[1]. This needs to be in theelseblock because we're returningarrwhich is defined in thatelse-block (and hence can't be accessed outside of it).
function countdown(n) {
return [n].concat(n > 1 ? countdown(n - 1) : []);
}
Here's one way to do it:
function countdown(n) {
if (n < 1) {
return [];
}
if (n === 1) {
return [1];
}
return [n].concat(countdown(n-1));
}
console.log(countdown(0));
console.log(countdown(1));
console.log(countdown(10));