Recursion - range of numbers

Tell us what’s happening:

I’m trying to solve the last recursion challenge and the code fails the test. When I put it to JS Fiddle, the code just returns an array with startNum and that’s it. For example:

console.log(rangeOfNumbers(1, 5)) / [1]

Could you give me a hint on what I’m doing wrong?

Your code so far


function rangeOfNumbers(startNum, endNum) {
// base case
var result = []
if (startNum > endNum) {
  return result;
// recursive case
} else {
  result.push(startNum);
  rangeofNumbers(startNum + 1, endNum);
}
return [];
};

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36.

Challenge: Use Recursion to Create a Range of Numbers

Link to the challenge:

Look at the spelling, the o is in lowercase while its supposed to be uppercase .

Yes, you have a typo in the code you posted as @divyanshtiwari pointed out. But even after fixing that typo your code will not work. You currently have two return statements in your code. The first one returns an empty array and the second one also returns an empty array. So you are never returning anything but an empty array. Also, the second one is outside of the else statement so it will never be used in the first place.

Start with your base case. When do you know that you can stop the recursion? You’ll have to read the instructions again carefully to figure it out. The base case will be the if statement in your code and it should return a valid return value for the function. Don’t worry about the else statement yet, just get the base case if statement working properly. You should be able to test it to verify that it works. After you think you have it working correctly you can post it here for verification.

Once you get the base case working then you can worry about the else statement where the recursion takes place. This one is a little tricky, so if you run into problems then you can ask for help with it at that time.

Your second return is also an empty array and out of else condition, put it in else and replace the array with result.

that’s not true, it will run after the code in the else statement run

the position of that one is not the issue, there are various other things that are issues but not that

for example the value returned from rangeofNumbers(startNum + 1, endNum); is never used

I did that but it doesn’t work anyway

I thought the base case (or when the code should stop running) is when startNum is larger than endNum. And when it is, the function should return the result, but now that’s what boggles me. What will the result be then? Let me think about it and get back to you.

Of course you’re right, I was accidentally thinking that the else statement was returning a value when it was not. It’s late and I’m old and confused.

This is a valid point, I was running the function in http://pythontutor.com/javascript.html#mode=display and I saw that it does return all the numbers that should be included in the resulting array, but I have no idea where they are kept in the process or how to access them; to me, it looks like the function just throws the numbers into the air but I have no idea how to catch them

you can start adding something like let returnedValue = rangeOfNumbers(startNum + 1, endNum) and then decide what to do with it, how does it need to be incorporated in result?

remember that the function returns an array with the range of numbers between the two arguments

@bbsmooth
So I have been thinking about the base case.
I HAD thought that the base case is when startNum is larger than the endNum, but now I think it doesn’t make sense. After all, I want the function to go from startNum to endNum and stop recursing once it reaches the endNum, do I not? If that is so, then it should stop the moment the startNum equals the endNum, like so:

// base case
if (startNum == endNum) {

Is that correct?

I have modified the beginning of the function so that it reads:

function rangeOfNumbers(startNum, endNum) {
  // base case
  var result = [];
  if (startNum == endNum) {
  result.push(endNum);
  return result;

And the code passes one test more, namely:

rangeOfNumbers(4, 4) should return [4].

Am I going in the right direction?

Yes you are. Your reasoning about the base case is dead on and that is the right if statement. The if block is now returning an array with a single value which is exactly what you want it to do. The test you are now passing is indeed a simple test for the base case. Congrats. If you want, you can clean up the if block a little and make it a one liner. But remember, it must always return an array with a single value. Also, I would recommend you get in the habit of always using the strict equality operator (===). It will save you some headaches down the road and most linters will complain if you use == instead of ===.

Now on to the recursion in the else statement. Keep in mind that this function returns an array of integers, so the return statement in the else block needs to return just that. While this is slightly tricky to figure out if you don’t have a lot of experience with recursion it can be solved in one fairly simple line of code. Obviously, because this is being solved with recursion, you are going to call the rangeOfNumbers() in the else block. Just remember that the goal here is to get to the base case (where both of the arguments are the same number), so each time rangeOfNumbers() is called, one of those arguments has to change in order to get you closer to the base case.

Don’t be afraid to add some console.log() statements in strategic places so you can see how the function is being called in the recursion, what the arguments are each time, and what is being returned each time.

1 Like

Thank you, @bbsmooth, this is fun and frustrating at the same time!

I am trying to write the recursive case and watch as it goes in a “code visualizer”. This is what I see:

Obviously the code doesn’t work the way it’s supposed to, but it looks like the function does call itself and does iterate, incrementing startNum by 1 each time, and it even pushes the number to the “result” array. But for some reason, the updated value of startNum ends up at index 0 of the array every time. Why? Isn’t push() supposed to append a value to the end of an array, instead of substituting it for another value?

Yes, the function is calling itself in the else block but the problem is that it isn’t returning anything in the else block.

Two things:

  • The else block must return an array of numbers, so you need a return statement in that block. Above I said that this can be done in one line of code. That should give you a hint about what you might want to return (i.e. you know you need to call the function itself in the else block and if you can do this in one line then I think you can figure out what should be included in the return statement :-).
  • Get rid of the result array completely (you don’t need it). To do this you’ll need to rewrite your if block so it doesn’t depend upon it (which should be easy to do).
  • Yes, Array.push() adds an item to the end of an array and it is possible to solve this challenge using it in the else block. But if you want to do this in one line of code then you’ll need to use a different Array method that allows you to join two arrays together (do you know which one that is?) This other method is very helpful in this instance because it returns a new array containing the two joined arrays.
1 Like

I don’t believe it! I have done it! My solution:

function rangeOfNumbers(startNum, endNum) {
  // base case
  if (startNum === endNum) {
  return [].concat(endNum);
  }
  // recursive case
  else {
  return [startNum].concat(rangeOfNumbers(startNum + 1, endNum));
  }
}

But I wouldn’t have done it without you.

Your hint about a different Array method prompted me to do some googling because the only Array methods that have been covered in the FCC curriculum up to this challenge are pop, push, shift, and unshift.

I’m not going to pretend I fully understand how this code works. I guess there are some things going on under the hood that I’m just not aware of yet. Some questions / takeaways that pop into my head as I write are, for example:

  • There must be a way to solve this using only the methods from the curriculum up until this challenge;
  • I don’t understand the difference between push() and concat() in this context, other than that concat() returns a new Array, whereas push appends to an existing one; but I still don’t see how this accounts for the fact that concat() works while push() does not;
  • I had thought about using new Array(), but it was not recommended over at w3schools;
  • I guess I understand how the recursive case works here, but when I look at the entire function, I think it should not work because when it gets to the base case it should ostensibly only return an Array with the endNum in it. How on Earth it manages to return an Array with the whole range of numbers is beyond me.

Thanks a million, @bbsmooth, you pushed me in the right direction!

Great job! I didn’t realize that the curriculum hadn’t covered Array.concat() yet so I apologize for leading you down that path. But at the same time that’s awesome that you were able to figure it for yourself!

Yes, you can use Array.push() to solve this and it only takes a few extra lines of code in the else block but personally I think it is conceptually easier to solve it with concat(). Also, since you are adding startNum to the beginning of the array you would need to use Array.unshift() instead of Array.push(). But in order to use unshift() you would need to create an extra temporary array variable in the else block and save the recursive function call to that variable, then add startNum to the beginning of that array with unshift(), then return the array. As for the reason you have to add these extra steps in order to use unshift(), I’ll direct you to the MDN docs:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift

The one sentence explanation at the top includes the reason. Remember, both the if and else blocks of your code have to return an array with numbers.

As to how this works, you should definitely take the time to understand it so you have a better feel for recursion. My suggestion would be to pencil it out on paper. Go through the steps on paper just like the computer goes through them. I’ll get you started.

If your args aren’t equal then you will hit the else block. So let’s say the original call is

rangeOfNumbers(1,3);

You will trigger the else block and the original return value will be

[1].concat(rangeOfNumbers(2,3))

Now you have the first number in your return array set to 1. And we know that the concat() method that calls rangeOfNumbers is going to return an array of numbers because rangeOfNumbers always returns an array on numbers.

So now just focus on rangeOfNumbers(2,3) in the original return value above. What is that going to return? You can replace rangeOfNumbers(2,3) in the original return value with the actual return value of rangeOfNumbers(2,3). And then keep doing this until you hit the base case. You should be able to see how the original return array is built up using recursion.