The Problem
Consider the following problem I came across on r/learnjavascript: Why does [1,3,11].map(parseInt)
result in [1, NaN, 3]
?
The Answer
It is common for new developers to be unaware of how map works and how it feeds parameters to the function it is provided. We're all aware that the first parameter it gives is the element in the array we're currently iterating over.
For example: [1,3,11].map(element => console.log(element))
gives us an output that lists 1, 3, 11.
But the fact is, map actually feeds three parameters to the given function. Map provides the current element being iterated over, the current index we're at, and the array being iterated over.
[1,3,11].map((element, index, array) => //... )
We can see this by changing our console.log by referring to it directly rather than wrapping it in an anonymous function:
[1,3,11].map(console.log)
1 0 [ 1, 3, 11 ]
3 1 [ 1, 3, 11 ]
11 2 [ 1, 3, 11 ]
This is pasted directly from the node REPL. You can see here, we get on each line, the element, the index, and the array.
So back to the original problem, why do we get [1, NaN, 3]
?
Because these three arguments are being passed into parseInt
. I believe that many new developers forget that parseInt
actually takes two arguments, more than they would forget that map
provides three arguments. parseInt
takes a number and the radix. The radix tells parseInt
which number system we're using. 0 is decimal, 2 is binary, 3 is trinary, 8 is octal, 16 is hex, and so on and so forth.
parseInt(11, 3)
outputs 2
because that's it's trinary equivalent.
So on each pass in map
, parseInt
looks like this:
parseInt(1, 0);
parseInt(3, 1);
parseInt(11, 2);
The array argument is ignored because parseInt
only takes two arguments. The number provided becomes the element from the array and the radix comes from the index.
On the first pass, we're converting 1 to decimal, which is of course, 1. A radix of 0 and 10 are decimal, so nothing changes with decimal numbers. On the second pass, we get NaN
because the value 1
is invalid for a radix. And on the third pass, we're converting 11
to 2
. 11
in binary is of course, 3
. We have a 1 in the 2^1 position and a 1 in the 2^0 position, 2^1 + 2^0 = 2 + 1 = 3.
In short, the reason we get unexpected values, is because map
provides more arguments to the parseInt
function than we desired.
How do we fix this?
The solution is very simple, we don't directly pass a reference to a function like parseInt
to map
. Instead, it's best to wrap it in an anonymous function first:
[1, 3, 11].map((element, index, array) => parseInt(element) );
Of course you can omit the
index
orarray
parameters if you aren't using them, I left them in for illustration
Then you can pass your arguments to the function as needed.
Now this is only necessary for functions that take, or potentially take, multiple arguments. If you want to map with a function that only takes one argument, feel free to pass it the reference directly.
const square = x => x * x;
[1,2,3].map(square);
// [1, 4, 9]
Just be careful to be sure whatever you pass into map
, and this goes for foreach
and many of the other array methods as well, only takes one argument.
You can read more about how parseInt
works on the Mozilla Developer Network here.
Happy coding.
Top comments (0)