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);
1

8 Answers 8

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.

Sign up to request clarification or add additional context in comments.

Comments

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;
 }
};

Comments

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 Comment

Thanks for naming your parameters, makes the function much more readable
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);

14 Comments

You can simplify the body to return x >= y ? [] : [x].concat(range(x+1, y));
@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.
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<y.
@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
I know the OP's code doesn't work. How is [].push([]) not defined?
|
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)];
}

1 Comment

was nice to explain the "why" to op
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):[];
};

Comments

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))

Comments

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
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.