-2

I'm working my way through eloquent javascript and for whatever reason return simply isn't working in the function nth. I honestly don't understand why it isn't working. I know I'm pretty close to getting the recursive version on nth working. This bug makes no sense to me. Here is all the relevant code:

function arrayToList(array) {
  list = null;
  for (i = 0; i < array.length; i++ ) {
    var x = array[array.length -(i + 1)];
    list = {value: x, rest: list};
  } return list;
}

function listToArray(list) {
  var array = [];
  var i = 0;
  for (var node = list; node; node = node.rest) {
    array[i] = node.value;
    i += 1;
  }
  return array;
}  


//return statements are broken here
function nth(list, number) {
 if (number == 0) {
   //console.log(list.value);
   return element;
   var element = list.value;
   } else if (number > 0) {
     list = list.rest;
     number--;
     //console.log(number);
     //console.log(list);
     nth(list, number);
   } else {
     return "Something went wrong";
     console.log(number);
   }
}

console.log(nth(arrayToList([10, 20, 30]), 1));

Can anyone explain why return statements aren't working in the in the nth function?

6
  • 1
    Your indentation seems a bit off...
    – unbindall
    Commented Jan 6, 2016 at 23:05
  • 1
    return nth(list, number); in else if Commented Jan 6, 2016 at 23:07
  • The function call is working correctly. I moved the indentation and it doesn't seem to be the issue.
    – user5451396
    Commented Jan 6, 2016 at 23:09
  • You should keep all those variables local with var: for (i=0 ...) is not eloquent.
    – RobG
    Commented Jan 6, 2016 at 23:13
  • I thought for( i = 0 ...) was local
    – user5451396
    Commented Jan 6, 2016 at 23:21

6 Answers 6

4

There are actually 2 problems with your code.
Look at the fiddle: https://jsfiddle.net/0wpdnn66/

  • You have to define element before using it (exchange lines 24 and 25)
  • You need to return from all branches. There is a return missing in line 31

function arrayToList(array) {
  list = null;
  for (i = 0; i < array.length; i++ ) {
    var x = array[array.length -(i + 1)];
    list = {value: x, rest: list};
  } return list;
}

function listToArray(list) {
  var array = [];
  var i = 0;
  for (var node = list; node; node = node.rest) {
    array[i] = node.value;
    i += 1;
  }
  return array;
}  


//return statements are broken here
function nth(list, number) {
 if (number == 0) {
   //console.log(list.value);
   var element = list.value;// <-- needs to be before return
   return element;
   } else if (number > 0) {
     list = list.rest;
     number--;
     //console.log(number);
     //console.log(list);
     return nth(list, number);// <-- needs return
   } else {
     return "Something went wrong";
     console.log(number);
   }
}

document.write(nth(arrayToList([10, 20, 30]), 1));

6
  • Thanks. Can you explain why I needed the return to call the function again? I don't understand why I need to make both of these changes, the function was calling itself correctly without it.
    – user5451396
    Commented Jan 6, 2016 at 23:16
  • Because otherwise you lose the result of the function (it returns undefined)
    – Ilya
    Commented Jan 6, 2016 at 23:17
  • it was calling itself, but not returning anything to your original call. The problem is that the recursively called function will (maybe) return a value to the function you called. Then the function you called from outside the recursive loop will have to return the result. Without a return statement, it will not do so.
    – Maru
    Commented Jan 6, 2016 at 23:18
  • Is there a resource that explains how return works? I didn't know that it did that
    – user5451396
    Commented Jan 6, 2016 at 23:19
  • Well, overlooking stuff happens to all of us ;)
    – Maru
    Commented Jan 6, 2016 at 23:19
2

You use return before assigning a value to element, which means because of hoisting element is declared, but undefined.

if (number == 0) {
   //console.log(list.value);
   return element;
   var element = list.value;

Should be

if (number == 0) {
   //console.log(list.value);
   var element = list.value;

   return element;

And since I guess you only defined element so that you could log it before returning, you could do this more simply (as Joe Frambach pointed out)

if (number == 0) {
   return list.value;

Also in the following else if it needs to return the nth's call result.

So that becomes

} else if (number > 0) {
     list = list.rest;
     number--;
     //console.log(number);
     //console.log(list);
     return nth(list, number);
8
  • 2
    or cut to the chase and return list.value
    – 000
    Commented Jan 6, 2016 at 23:10
  • Yup seems legit here
    – axelduch
    Commented Jan 6, 2016 at 23:10
  • Thanks for the response. I corrected that and return still doesn't work
    – user5451396
    Commented Jan 6, 2016 at 23:11
  • @Steve Updated my answer
    – axelduch
    Commented Jan 6, 2016 at 23:14
  • That solution does work, but I don't understand why I need to return the call
    – user5451396
    Commented Jan 6, 2016 at 23:17
1

You need to make two changes:

function nth(list, number) {
  if (number == 0) {
    var element = list.value; // <- this goes above the return
    return element;
  } else if (number > 0) {
    list = list.rest;
    number--;
    return nth(list, number); // <- you need to add a return statement here
  } else {
    return "Something went wrong";
  }
}

With this change:

console.log(nth(arrayToList([10, 20, 30]), 0)); // returns 10
console.log(nth(arrayToList([10, 20, 30]), 1)); // returns 20
console.log(nth(arrayToList([10, 20, 30]), 2)); // returns 30

You are basically iterating over a linked list using recursion. number is the number of nodes that you want to skip. If number is 0, we want to return that value. If number is 1, we want to skip one node. This is what the else if part of your code is doing. It's skipping the current node, and returning the remainder of the nodes.

To jump to the next node you do nth(list.rest, --number). You do this continually until number reaches 0, which gets you the value you actually want.

Eventually when number is 1, the return statement in the else if part of your code will be return nth(list.rest, 0), which returns the actual value you want. And this is why you return that value.

Just for fun, here's a version that doesn't use recursion:

function nth(list, number) {
  if (number < 0) return;

  for (var curr = list; curr && number > 0; number--) {
    curr = curr.rest;
  }

  if (curr) return curr.value;
}
2
  • Thanks. Can you explain why I needed the return to call the function again? I don't understand why I need to make both of these changes, the function was calling itself correctly without it.
    – user5451396
    Commented Jan 6, 2016 at 23:15
  • You are basically iterating over a linked list using recursion. Number is the number of nodes that you want to skip... so to jump to the next node you do nth(list.rest, --number)... and you do this continually until number reaches 0. And thats the value you eventually want. Because of recursion, the return statement will eventually return the correct value, because you will eventually get to nth(list.rest, 0) which returns the actual value.
    – br3nt
    Commented Jan 6, 2016 at 23:21
0

Something like this will work

function arrayToList(array) {
    var list = null;
    for (var i = 0; i < array.length; i++) {
        var x = array[array.length - (i + 1)];
        list = {
            value: x,
            rest: list
        };
    }

    return list;
}

//return statements are broken here
function nth(list, nr) {
    if (nr === 0) {
        return list.value;
    } else if (nr > 0) {
        list = list.rest;
        nr--;
        return nth(list, nr);
    }
}

var list = arrayToList([10, 20, 30]);
var nthNr = nth(list, 1);
console.log(nthNr);
0

In order to return "something went wrong" you need to check if list is not null before accessing its properties. Effectively this checks to see if number has not exceeded its upper bound and avoids throwing a TypeError if it does. For example and including a check on the lower bound of number,

function nth(list, number) {
    if( number < 0 || !list)
        return "Something went wrong";
    if (number == 0) {
        return list.value;
    }
    return nth(list.rest, --number);
 }

Issues of setting element before the return statement (not used above) and returning values from recursive calls (as above) have already been covered in comments and answers.

0

You can prevent both issues (var element declared at the wrong place and missing return) by removing the var and using a ternary expression. That's a general coding style advice: less var and less return => less trouble. Your function boils to a one-liner:

function nth(list, number) {
  return number <= 0 ? list.value : nth(list.rest, number-1);
}
4
  • Man... that downvote without explanation is not helpful.
    – Ilya
    Commented Jan 6, 2016 at 23:23
  • Not my down vote, but code–only answers aren't helpful. You need to explain why the OP has their issue and how your code fixes it.
    – RobG
    Commented Jan 7, 2016 at 0:05
  • It should be nth(list.rest, --number) as number-- decrements the number after passing the variable value to the function. Also, a statement on the first line such as if (number < 0 || !list) return; will avoid index out of bounds issues. Also not my downvote
    – br3nt
    Commented Jan 7, 2016 at 0:06
  • @RobG @br3nt thanks for your comments, I'll improve the answer. I thought that as the issues had already been explained, it was not necessary to repeat them. @br3nt " index out of bounds issues": if number < 0 nth will returns undefined, so no need for a specific check.
    – Ilya
    Commented Jan 7, 2016 at 8:14