JS Array.map gotcha
I was working on a project today and was seeing weird sort behavior and couldn’t figure out what was going on for a while. I finally discovered that JavaScript Array.map wasn’t returning the values I expected. In particular:
> "11.4.402.265".split('.').map(parseInt) [11, NaN, NaN, 2]
I was a little worried that Mozilla was broken, so I asked the JS experts in IRC and got this interesting diagnosis from Jeff Walden (Waldo):
parseInt takes a second parameter that’s a radix; map calls its function with (value, index, thisArray)
This magic behavior of Array.map can be useful if you want to do even-odd behavior or other tasks, but it happened to be my personal footgun today. Be careful! My code now interposes its own function:
a.split('.').map(function(i) { return parseInt(i); });
I’m not sure I really want to know why parseInt(11, 0) returns 11.
November 15th, 2012 at 4:35 pm
> I’m not sure I really want to know why parseInt(11, 0) returns 11.
I’d guess the code does something like “if (!radix) radix = 10;”.
November 15th, 2012 at 7:09 pm
I’m going to tell you anyways. parseInt treats undefined or 0 radix as 10.
November 15th, 2012 at 8:24 pm
In this case, were you only have numbers separated by dots, you could use the Number constructor:
“11.4.402.265”.split(‘.’).map(Number)
Or a simple lambda, in Gecko-proprietary syntax:
“11.4.402.265”.split(‘.’).map(function(x) +x)
Cesar is wrong, undefined or 0 is not treated as 10, it actually makes parseInt try to figure out the format, e.g. initial zero would switch it to octal:
parseInt(“0100”, 10); // 100
parseInt(“0100”, 8); // 64
parseInt(“0100”, 0); // 64
parseInt(“0100”); // 64
parseInt(“100”); // 100
November 15th, 2012 at 10:41 pm
I suppose this is also a somewhat non-obvious risk for when one considers adding optional arguments to an existing API. E.G., consider if parseInt only took a single argument today, and there was discussion of adding an optional radix arg. Hmm.
November 16th, 2012 at 12:37 am
@Cesar, it’s not always 10-base. `parseInt(’09’)` results 0 (in octal, but 09 is invalid) not 9.
A safe way to call should supply the base, if an input like `010` may be used.
I have written about it when I saw a Gist: http://blog.yjl.im/2012/02/javascript-map-and-parseint.html
November 16th, 2012 at 6:32 am
No, Cesar, 0 means “guess the radix”, so if the string starts with (+/-) 0x then hex is assumed otherwise if it starts with (+/-) 0 then octal is assumed otherwise decimal.
So for instance, parseInt(“-011”, 0) is minus nine, while parseInt(“-09”, 0) is zero!
Note that this is actually correct behaviour for parsing IPv4 addresses, e.g. 0177.0.0.1 is loopback.
November 16th, 2012 at 10:50 am
Remember to pass in the radix parameter every time:
a.split(‘.’).map(function(i) { return parseInt(i, 10); });
November 18th, 2012 at 2:10 am
The Google Closure JavaScript compiler’s “standard library”, which includes types for JavaScript builtins, specifies the type of parseInt() as taking two arguments for this reason — makes it a compiler error when you forget (which I still do all the time).