Suppose I have this simple JavaScript function:
function returnArray(){
return [1, 2, 3];
}
Further suppose that I then say
var test = [0, ...returnArray()];
You'd expect test to be equal to [0,1,2,3], and you'd be right. I tried that and of course it works.
Now I have this exercise where I'm suppose to build a function called double that takes an array as a parameter and returns another array that contains all of the original array's values doubled. So if I call double([1,2,3]) I should get [2,4,6]. The constraints of the exercise are that I must use only array destructuring, recursion, and the rest/spread operators to build my function. No array helpers allowed. So I came up with this:
function double(array){
if (array.length===1) return 2*array[0];
var [num, ...rest] = array;
if (rest.length!=0) return [2*num, ...double(rest)];
}
If I run this function with any array whose size is at least two, I receive an error message saying that double is not a function. If I remove the ... operator before double, magically double is a function again, except that of course the result of double([1,2,3]) is [2,[4,6]], which is not quite the same as [2,4,6].
My first thought was that maybe, for some weird reason, you can't use ... in front of a function, even if the function returns an array, so I tested this hypothesis with my the returnArray() function above, and found out that it works just fine. I have no idea why it breaks down in the exercise. I can only guess recursion might have something to do with it, but I have no clue why it would be so. Can anyone point out what's wrong my my code?
EDIT: Thanks everyone, it was quite a silly mistake! I should have seen it. I added comments to some of your answers.
It's a very strange error message, no question, but the main problem is a logical error in double
: In two branches of the code, calling double
results in a non-iterable value (in one case a number, in another undefined
). But you're always applying spread notation to it. So that fails in those two cases.
The cases are:
- You're only returning the number, not an array, in the
array.length === 1
case.
- You're not returning anything in the case where
array.length
is not 1 and rest.length
is 0, so the result of calling double
in that case is undefined
.
You're hitting the case where you try to spread a number first, like this:
function a() {
return 42;
}
const b = [...a()];
For #1, you should be returning an array with one entry. For #2, You should be returning []
. So the minimal changes version is:
function double(array) {
if (array.length === 1) {
return [2*array[0]];
// ^−−−−−−−−−−^−−−−−−−−−−− note
}
var [num, ...rest] = array;
if (rest.length > 0) {
return [2*num, ...double(rest)];
}
return []; // <−−−−−−−−−−−−−−−−− note
}
console.log(double([1,2,3]));
Your logical error has been pointed out in comments and answers, but let me point out a cleaner, simpler, less bug-prone way to write this which is more in line with basic principles of recursion.
function double([head, ...tail]) {
if (head === undefined) return [];
return [2*head, ...double(tail)];
}
In other words, there is only one "base case", namely the empty array, which returns an empty array. Everything else is simple recursion.
You could "functionalize" this further with
function map(fn) {
return function iter([head, ...tail]) {
return head === undefined ? [] : [fn(head), ...iter(tail)];
};
}
const double = map(x => 2*x);
console.log(double([1, 2, 3]));
Just a minor change:
function double(array) {
// note the return here is an array, not a number
if (array.length === 1) return [2 * array[0]];
var [num, ...rest] = array;
if (rest.length) return [2 * num, ...double(rest)];
}
console.log(double([1, 2, 3, 4]));
You were returning a number, and destructuring a number will leave you with an error.
...5 // throws SyntaxError
Here is the error I get using node@6.10.3
if (rest.length!=0) return [2*num, ...double(rest)];
^
TypeError: double(...)[Symbol.iterator] is not a function
at double (/home/henrique/labs/test.js:4:41)
at double (/home/henrique/labs/test.js:4:41)
at Object.<anonymous> (/home/henrique/labs/test.js:7:1)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:390:7)
Which probably means that the result of the evaluation of some of your function call is not an iterable.
The error is here:
if (array.length===1) return 2*array[0];
Change to:
if (array.length===1) return [2*array[0]];
and it will work.