337

From the Mozilla Developer Network:

[1,4,9].map(Math.sqrt)

will yield:

[1,2,3]

Why then does this:

['1','2','3'].map(parseInt)

yield this:

[1, NaN, NaN]

I have tested in Firefox 3.0.1 and Chrome 0.3 and just as a disclaimer, I know this is not cross-browser functionality (no IE).

I found out that the following will accomplish the desired effect. However, it still doesn’t explain the errant behavior of parseInt.

['1','2','3'].map(function(i){return +i;}) // returns [1,2,3]
7
  • 16
    For lazy's: use .map(parseFloat) because it takes ony one parameter. Commented Aug 31, 2013 at 11:29
  • 70
    Or use .map(Number). Commented Sep 22, 2013 at 17:26
  • 2
    you can arr.map(Math.floor) if you want Integers without a hand-rolled function. Commented Oct 2, 2014 at 22:57
  • @Nikolai user669677 great suggestions ! I'd upvote that in an anwser Commented Sep 30, 2015 at 12:48
  • can anyone explain why parseInt parses first number correctly and makes error for other than first index Commented Apr 3, 2018 at 7:33

8 Answers 8

546

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

Sign up to request clarification or add additional context in comments.

Comments

29

map is passing along a 2nd argument, which is (in many of the cases) messing up parseInt's radix parameter.

If you're using underscore you can do:

['10','1','100'].map(_.partial(parseInt, _, 10))

Or without underscore:

['10','1','100'].map(function(x) { return parseInt(x, 10); });

Comments

26

You could solve this problem using Number as iteratee function:

var a = ['0', '1', '2', '10', '15', '57'].map(Number);

console.log(a);

Without the new operator, Number can be used to perform type conversion. However, it differs from parseInt: it doesn't parse the string and returns NaN if the number cannot be converted. For instance:

console.log(parseInt("19asdf"));
console.log(Number("19asf"));

1 Comment

A difference that prevents this solution for working for my use case: [""].map(Number) results in [0] whereas with parseInt it results in [NaN]. In my case I want to filter out anything that can't be parsed.
13

I'm going to wager that it's something funky going on with the parseInt's 2nd parameter, the radix. Why it is breaking with the use of Array.map and not when you call it directly, I do not know.

//  Works fine
parseInt( 4 );
parseInt( 9 );

//  Breaks!  Why?
[1,4,9].map( parseInt );

//  Fixes the problem
[1,4,9].map( function( num ){ return parseInt( num, 10 ) } );

2 Comments

parseInt only assumes octal if the supplied string starts with a 0 character.
Oh ya... that's right. It tries to "guess" the radix based on the input. Sorry about that.
3

another (working) quick fix :

var parseInt10 = function(x){return parseInt(x, 10);}

['0', '1', '2', '10', '15', '57'].map(parseInt10);
//[0, 1, 2, 10, 15, 57]

Comments

3

You can use arrow function ES2015/ES6 and just pass number to the parseInt. Default value for radix will be 10

[10, 20, 30].map(x => parseInt(x))

Or you can explicitly specify radix for better readability of your code.

[10, 20, 30].map(x => parseInt(x, 10))

In example above radix explicitly set to 10

Comments

-1

parseInt IMHO should be avoided for this very reason. You can wrap it to make it more safe in these contexts like this:

const safe = {
  parseInt: (s, opt) => {
    const { radix = 10 } = opt ? opt : {};
    return parseInt(s, radix);
  }
}

console.log( ['1','2','3'].map(safe.parseInt) );
console.log(
  ['1', '10', '11'].map(e => safe.parseInt(e, { radix: 2 }))
);

lodash/fp caps iteratee arguments to 1 by default to avoid these gotchas. Personally I have found these workarounds to create as many bugs as they avoid. Blacklisting parseInt in favor of a safer implementation is, I think, a better approach.

Comments

-1

You can solve that issue like this:

array.map(x => parseInt(x))

Example:

var arr = ["3", "5", "7"];

console.log(
  arr.map(x => parseInt(x))
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.