4

I am trying to get the range of numbers using recursion. Can someone explain to me why it isn't working?

function range(x,y){
    var results = [];
    if(x === y){
        return results;
    }



return  results.push(range(x + 1,y));
}

range(1,5);
joe
  • 2,468
  • 2
  • 12
  • 19
spaceDog
  • 441
  • 1
  • 7
  • 22
  • push [doesn't return an array](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push). – 1983 Jul 04 '16 at 01:11

8 Answers8

6

The beauty of recursion is that you don't need local variables (var results). You just pass state as arguments to each recursive iteration:

const concat = (xs, y) => xs.concat(y);

const range = (x, y) => {
  const rec = (x, y, acc) => x < y ? rec(x + 1, y, concat(acc, x)) : acc;
  return rec(x, y, []);
}

ES5 version in case you aren't familiar with the arrow syntax:

function concat(xs, y) {
  return xs.concat(y);
}

function range(x, y) {
  function rec(x, y, acc) {
    return x < y ? rec(x + 1, y, concat(acc, x)) : acc;
  }

  return rec(x, y, []);
}

That isn't the most elegant solution though!

With recursion we can simply build up the stack with each recursive call. Each stack frame contains a computed partial result. Then we just need to unwind the stack and attach each partial result to an array:

const range = (x, y) => x < y ? [x].concat(range(x + 1, y)) : [];

Or more functional:

const concat = (xs, y) => xs.concat(y);
const range = (x, y) => x < y ? concat([x], range(x + 1, y)) : [];

Note that concat([x], range(x + 1, y)) is the recursive case and [] the base case.

5

Try this:

function rangeOfNumbers(startNum, endNum) {
 if (startNum - endNum === 0) {
  return [startNum];
 } else {
  const numbers = rangeOfNumbers(startNum + 1, endNum);    
  numbers.unshift(startNum);
  return numbers;
 }
};
Zhivko Zhelev
  • 321
  • 4
  • 10
2

Solution: Solved this recursion problem, which is taking 2 numbers as input and returning back the array which contains range of the numbers inclusive of the startNumber and EndNumber

Assumption-> end_num is always greater than start_num

function rangeOfNumbers(start_num, end_num) {
    if(start_num!==end_num){
        let numbers = rangeOfNumbers(start_num+1,end_num);
        numbers.unshift(start_num);
        return numbers;
    }
    else
        return [start_num];
    };
1

Results will be always empty since you actually don't put anything in it.

What would work is this

function range(x,y){
    var results = [];
    if(x === y){
        return results;
    }
    results.push(x);
return  results.concat(range(x + 1,y));
}

range(1,5);
MoustafaS
  • 1,991
  • 11
  • 20
  • 1
    You can simplify the body to `return x >= y ? [] : [x].concat(range(x+1, y));` – 1983 Jul 04 '16 at 00:54
  • @1983 nice one ;) But I left it as it was and just fixed the code, as I thought he might need the explanation, so didn't want to complicate things for him. – MoustafaS Jul 04 '16 at 01:00
  • Sure, was just commenting. `results` in the OP's code isn't empty though: the return value from `push` is pushed onto it, for `x – 1983 Jul 04 '16 at 01:19
  • @1983 Try it in the console and see. It wasnt working, since the code actually never adds anything to results, so it was always empty on all levels, besides, [].push([]) is not defined, he should have used concat – MoustafaS Jul 04 '16 at 01:21
  • I know the OP's code doesn't work. How is `[].push([])` not defined? – 1983 Jul 04 '16 at 01:28
  • @1983 Because actually if we have some array x with value a and you call x.push([y,z]), the result will be x = [a, [y,z]]. So its not actually what he wants here – MoustafaS Jul 04 '16 at 01:31
  • That's not my point. Your remark that 'Results will be always empty since you actually don't put anything in it' isn't correct. I'm not complaining about your code. – 1983 Jul 04 '16 at 01:33
  • Actually, please revise the code, it will always be empty, if it will be holding the result of range(x+1,y) then what is the return of that function ? its also empty, etc... – MoustafaS Jul 04 '16 at 01:34
  • `results` is only empty if `x===y`. Otherwise a value is pushed onto it, and `push` returns a number. – 1983 Jul 04 '16 at 01:36
  • @1983 Can you please tell me at which line does something get added to the array ? Do you know how to trace recursive calls ? – MoustafaS Jul 04 '16 at 01:38
  • The line that says `push`. – 1983 Jul 04 '16 at 01:39
  • It won't push anything, since it always pushes [] or range(somenumber,somenumber), which in turn pushes [] or range(x,y) etc... – MoustafaS Jul 04 '16 at 01:41
  • It pushes `[]`, which is something, or the number 1. – 1983 Jul 04 '16 at 01:43
  • Try it at the console and see please – MoustafaS Jul 04 '16 at 01:45
1

Let's firstly try to answer your "why" question before we give a solution because none of these answers explain your "why" question.

When you return results.push(<any argument>) the return value is the length of the array after the push. On the first call in your example, x does not equal y, so we are returning the call to push. You can think of it like this:

return array.push(<anything>) is going to be the same as:

array.push(<anything>)
return array.length

Therefore, you will always return the number 1 from this because the length of the array when you push the function call to it is 1. The content of that array will be another array that's nested all the way to the n levels deep where n is the range, but it's length will still be one and you will never see the content of this array unless you did it this way:

results.push(rangeOfNumbers(x+1, y))
return results;

In your example rangeOfNumbers(1, 5), if you logged that return value it would look like this:

[ [ [ [ [ ] ] ] ] ]

I solved it this way, but I like the functional solution that was posted by another user more:

function rangeOfNumbers(s, e) {
  return s == e ? [s] : [s, ...rangeOfNumbers(s+1, e)];
}
Joshua Wood
  • 435
  • 5
  • 18
0
 function rangeOfNumbers(startNum, endNum) {
      if (startNum>endNum){
      return [];
      }
      else{
        const range = rangeOfNumbers(startNum+1, endNum);
         range.unshift(startNum);
         return range;
      }
    };

//or more simple

function rangeOfNumbers(startNum, endNum) {
  return endNum>=startNum?rangeOfNumbers(startNum,endNum-1).concat(endNum):[];
};
Dennis Paixao
  • 139
  • 1
  • 3
0
function rangeOfNumbers(firstNum, lastNum) {
    if (firstNum - lastNum === 0) {
    return [lastNum];
  } else {
    let rangeArray = rangeOfNumbers(firstNum, lastNum - 1);
    rangeArray.push(lastNum);
    return rangeArray;
  }
}

console.log(rangeOfNumbers(1, 5))
0

Lots of clever solutions posted, but I think this is a use case for the plain old 'for loop'. It's easier to see what's happening, and it will be easier for new devs on your team. My example is inclusive (it will include the min value and the max value), and it has an optional step parameter which will default to 1 if not passed in.

function range(min, max, step = 1) {
  let arr = []
  for (let i = min; i <= max; i = i + step ) {
    arr.push(i)
  }
  return arr
}
McGrew
  • 812
  • 1
  • 9
  • 16