Why does parseInt returns NaN when used with Array.map?
Because map adds a second and third parameters: the index and the array itself. See:
["1234-04-23", "1234", "04", "23"].map(console.log);
// => 1234-04-23 0 ["1234-04-23", "1234", "04", "23"]
// => 1234 1 ["1234-04-23", "1234", "04", "23"]
// => 04 2 ["1234-04-23", "1234", "04", "23"]
// => 23 3 ["1234-04-23", "1234", "04", "23"]
As you can see in the specification, parseInt
also accepts a second parameter as base, so you are actually doing this:
console.log(["1234-04-23", "1234", "04", "23"].map((e, e2) => parseInt(e, e2)));
// => [1234, NaN, 0, 2]
The NaN
is because:
parseInt
stops at the first invalid character and returns whatever it
has at that point. If there are no valid characters to parse, it
returnsNaN
Source: https://stackoverflow.com/a/39147168/1525495
Why does parseInt yield NaN with Array#map?
The callback function in Array.map
has three parameters:
From the same Mozilla page that you linked to:
callback is invoked with three arguments: the value of the element, the index of the element, and the Array object being traversed."
So if you call a function parseInt
which actually expects two arguments, the second argument will be the index of the element.
In this case, you ended up calling parseInt
with radix 0, 1 and 2 in turn. The first is the same as not supplying the parameter, so it defaulted based on the input (base 10, in this case). Base 1 is an impossible number base, and 3 is not a valid number in base 2:
parseInt('1', 0); // OK - gives 1
parseInt('2', 1); // FAIL - 1 isn't a legal radix
parseInt('3', 2); // FAIL - 3 isn't legal in base 2
So in this case, you need the wrapper function:
['1','2','3'].map(function(num) { return parseInt(num, 10); });
or with ES2015+ syntax:
['1','2','3'].map(num => parseInt(num, 10));
(In both cases, it's best to explicitly supply a radix to parseInt
as shown, because otherwise it guesses the radix based on the input. In some older browsers, a leading 0 caused it to guess octal, which tended to be problematic. It will still guess hex if the string starts with 0x
.)
Why does JavaScript's map and parseInt function return NaN every other call?
According to the documentation, parseInt
accepts two parameters: string integer and base. At the same time, map
passes two arguments: value and index.
So, basically your call is equivalent to this:
["6", "1", "0", "3", "3"].map((value, index) => parseInt(value, index))
which results into NaN
when it is not possible to parse it with given parameters.
Particularly:
parseInt("6", 0)
is working, because it seem to use default base (10)parseInt("1", 1)
is failing, because 1 is invalid baseparseInt("0", 2)
is working, it is a binary representation of decimal 0parseInt("3", 3)
is failing, because there can be no "3" in base 3parseInt("3", 4)
is working, it is a 4-base representation of decimal 3
JavaScript Array.map(parseInt) causes NaN error
parseInt
is often used with one argument, but takes two.
The first is an expression and the second is the radix.
To the callback function, Array.prototype.map passes 3 arguments to parseInt
: the element, the index, the array.
The third argument is ignored by parseInt
, but not the second one, hence the possible confusion.
Why does using Array.map(parseInt) on an array of strings produce different results
JavaScript is often the subject of parody, for its seemingly unexpected results.
var a = []+{} // [Object object]
var b = {}+[] // 0
However there is consistency in its madness, and I suspected the parseInt
behavior must have some reason behind it.
Getting to the bottom of what's happening
I first thought of debugging parseInt
, but since couldn't debug a native function, I thought of wrapping it around another function that basically does the same thing.
var a = ['10','10','10','10']
var intParse = function (x) {
return parseInt(x);
};
console.log(a.map(parseInt)); // [10, NaN, 2, 3, 4]
console.log(a.map(intParse)); // [10,10,10,10]
Ok so it seems like everything is working fine
But just for the sake of brevity I decided to try some more observations
var a;
(a = Array(13).join('10,').split(',')).pop() // try array of 13 '10's
a.map(parseInt); // [10, NaN, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
(a = Array(10).join('100,').split(',')).pop() // try array of 10 '100's
a.map(parseInt); // [100, NaN, 4, 9, 16, 25, 36, 49, 64, 81]
(a = Array(10).join('7,').split(',')).pop() // try array of 10 '6's
a.map(parseInt); // [7, NaN, NaN, NaN, NaN, NaN, NaN, 6, 6, 6]
Maybe it's not that weird after all
At this point as weird as the results may seem, they are consistent (in some way), there certainly seems to be a pattern.
It then hit me.Array.map(callback)
the callback takes 3 parameters, (key, index, array)
, so what if parseInt
doesn't just take one parameter but 2 instead.
That would certainly had an effect on its results
Turns out The parseInt()
function parses a string argument and returns an integer of the specified radix or base.
SyntaxparseInt(string, radix);
the radix is the base of the number
parseInt("10", 0) // 10, zero meant decimal
parseInt("10", 1) // NaN, since only 0 is allowed in base 1
parseInt("10", 2) // 2, '10' in binary
parseInt("10", 3) // 3, '10' in ternary
//...
Since the second argument in map
's callback is the index the radix kept changing according to the index.
This explains why my intParse
function worked.
I had specifically defined that it uses 'parseInt' with just x.
I thought this was what's happening inside map
var intParse = function (x) { return parseInt(x);}
When in fact this is what was happening
var intParse = function (x, r, array) { return parseInt(x, r);}
What I should've done when wrapping the function was
to not assuming the arguments that where being passed like so
var a = ['10','10','10','10']
var intParse = function () {
return parseInt.apply(this, arguments);
}
console.log(a.map(parseInt)); // [10, NaN, 2, 3, 4]
console.log(a.map(intParse)); // [10, NaN, 2, 3, 4]
Lessons learned
This was a nice exploration, I think I wound up learning a bit more than I thought I would about parseInt
.
More importantly tho I was reminded that when programs act in an unexpected way, it is most likely for a reason.
Finally, if one wants to properly wrap a function use .apply(this, arguments)
Javascript: Inconsistent behavior with bound function in `some` array function
Array.some()
passes 3 arguments to the callback function: the current array element, its index, and the array.
String.endswith()
takes an optional second argument, which is used as the length of the string instead of its actual length.
So the first iteration calls
f('s', 0, ['s', 'q', 'q'])
which is equivalent to
"sss".endsWith('s', 0)
If you use 0
as the length of the string, it doesn't end with s
, so this returns false
.
When you use your function value => f(value)
, the extra arguments are discarded so the default length is used and the function operates as expected.
Related Topics
Parsing a String to a Date in JavaScript
Why Does a Regexp With Global Flag Give Wrong Results
How to Sort an Object Array by Date Property
Convert a Unix Timestamp to Time in JavaScript
How to Create a Two Dimensional Array in JavaScript
Loading Cross-Domain Endpoint With Ajax
What Is the Use of the JavaScript 'Bind' Method
Negative Lookbehind Equivalent in JavaScript
Addeventlistener Calls the Function Without Me Even Asking It To
JavaScript Get Clipboard Data on Paste Event (Cross Browser)
JavaScript: Client-Side Vs. Server-Side Validation
Canvascontext2D Drawimage() Issue [Onload and Cors]
How to Delay the .Keyup() Handler Until the User Stops Typing
Is There a Cross-Browser Onload Event When Clicking the Back Button
How to Get the Full Object in Node.Js'S Console.Log(), Rather Than '[Object]'