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.

Atom Feed for Comments 8 Responses to “JS Array.map gotcha”

  1. squib Says:

    > 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;”.

  2. Cesar Says:

    I’m going to tell you anyways. parseInt treats undefined or 0 radix as 10.

  3. marcoos Says:

    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

  4. Justin Dolske Says:

    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.

  5. Yu-Jie Lin Says:

    @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

  6. Neil Rashbrook Says:

    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.

  7. Bill Barry Says:

    Remember to pass in the radix parameter every time:

    a.split(‘.’).map(function(i) { return parseInt(i, 10); });

  8. Evan M Says:

    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).

Leave a Reply