Live data from Hacker News

Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

medium.com

31–40 of 163 posts

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#31
post #18
post #2

Stuff like this is what drives me crazy when I work with dynamic languages like JS or PHP. I have seen a lot of code that looked perfectly fine but suffered from unexpected casts or defaults. I much prefer languages like C#, C++ or TypeScript where the compiler warns me of such problems.

> I much prefer languages like C#, C++ or TypeScript where the compiler warns me of such problems. parseInt is defined as accepting a string and an optional radix value, which is numeric. map is defined as providing the value and its index, which is also numeric. Would any of C#, C++, or TypeScript catch that without redefining either parseInt or map to require a more specific type, breaking compatibility with many m…

> Would any of C#, C++, or TypeScript catch that

C# would have a compile error with that map and parseInt definition because it can't coerce the types.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#32
post #4

This isn't strange or surprising. parseInt takes two arguments, the second one is the radix and map will call with three arguments, the value, the index and the whole array. You just have to know this and it might be different in other languages. [ ... ].map(x => ...) is the right way to do this.

Yeah, I’m not surprised at all by this. I get that there are a lot of weird things in JS but I don’t think this is a JS oddity. Surprise! You need to know the basics of how your language works.

That's apologist talk pure and simple. The language, silently, does something that's almost certainly wrong. The language has enough information to provide you with a helpful warning or error message that you probably don't want to do this, but instead, it violates the principle of least surprise by just doing the wrong thing instead.

The correct error is something along the lines of:

  let numbers = input.iter().map(parseInt).collect::>();

    --> src/main.rs:10:32
     |
  4  | fn parseInt(input: T, radix: u32) -> Option where T: AsRef {
     | ----------------------------------------------------------------------- takes 2 arguments
  ...
  10 |     let numbers = input.iter().map(parseInt).collect::>();
     |                                ^^^ expected function that takes 1 argument
This isn't rocket science, it's basic language design. From time immemorial engineers have been making mistakes as they write software, so compilers evolved not to pretend otherwise and hope for the best, but to help engineers catch them. Except for one.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#33
post #4

This isn't strange or surprising. parseInt takes two arguments, the second one is the radix and map will call with three arguments, the value, the index and the whole array. You just have to know this and it might be different in other languages. [ ... ].map(x => ...) is the right way to do this.

Yeah, I’m not surprised at all by this. I get that there are a lot of weird things in JS but I don’t think this is a JS oddity. Surprise! You need to know the basics of how your language works.

IMO the real oddity here is that Javascript works like this.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#34
post #28

Disclaimer: I don't know Javascript, but that's why: > ['1','7','11'].map(console.log) 1 0 [ '1', '7', '11' ] 7 1 [ '1', '7', '11' ] 11 2 [ '1', '7', '11' ] [ undefined, undefined, undefined ] > parseInt(1,0) 1 > parseInt(7,1) NaN > parseInt(11,2) 3 The correct way is: > ['1','7','11'].map(x => parseInt(x)) [ 1, 7, 11 ] same as: > ['1','7','11'].map(x => parseInt(x, 10)) [ 1, 7, 11 ]

Those are unfortunately not the same. The second one (with an explicit radix of 10) is correct; the first is not (or is at least a bit riskier). See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... (ctrl+f for "always").

In most browser implementations (at least all the ones we develop for) it would assume a radix of 10 for those values specifically.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#36
post #17

Easy fix ['1', '7', '11'].map(Number)

Agreed. Number is not only more obvious than parseInt for other coders but also defaults to base 10, whereas parseInt (at least historically) does not (edit: ...if you've got leading zeros, see post below).

> defaults to base 10, whereas parseInt (at least histortically) does not.

To clarify for others (because this probably sounds crazy): The default is implicit like the rest of JavaScript, it will change base depending on presence of the prefixes 0x (16) and 00 (8), it doesn't seem to have included 0b yet. The confusing bit was octal because as you can imagine some sources might have base 10 padded with zeros, ES5 basically removes implicit octals in parseInt to avoid this issue.

Arguably this is more of a problem with the ambiguous octal prefix than the concept of using prefixes to determine base.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#37

>> "If the radix provided is falsy, then by default, radix is set to 10." The official docs for parseInt says this: >> An integer between 2 and 36 that represents the radix (the base in mathematical numeral systems) of the string. Be careful — this does not default to 10. [1] I just found it confusing whether the author meant the default value is 10, or if a falsy parameter (not undefined) turns out to be 10. [1] htt…

Agreed - if you read the description section in the Mozilla docs, there is no mention of falsy. He should eliminate that section.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#38

Disclaimer: I don't know Javascript, but that's why: > ['1','7','11'].map(console.log) 1 0 [ '1', '7', '11' ] 7 1 [ '1', '7', '11' ] 11 2 [ '1', '7', '11' ] [ undefined, undefined, undefined ] > parseInt(1,0) 1 > parseInt(7,1) NaN > parseInt(11,2) 3 The correct way is: > ['1','7','11'].map(x => parseInt(x)) [ 1, 7, 11 ] same as: > ['1','7','11'].map(x => parseInt(x, 10)) [ 1, 7, 11 ]

Thank you! The article failed to provide the conclusion, ie, how to make it work as one would expect were one not steeped in JS quirks

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#39
post #4

This isn't strange or surprising. parseInt takes two arguments, the second one is the radix and map will call with three arguments, the value, the index and the whole array. You just have to know this and it might be different in other languages. [ ... ].map(x => ...) is the right way to do this.

So it's clickbait?

The article is not clickbait but the takeaway is easily misinterpreted. Instead of "JavaScript is such a weird and confusing language" it should be "a lot of people are using JavaScript without understanding the method signatures of commonly using methods".

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#40
post #21
post #3

To quote the tl;dr: ['1', '7', '11'].map(parseInt) doesn’t work as intended because map passes three arguments into parseInt() on each iteration. The second argument index is passed into parseInt as a radix parameter. So, each string in the array is parsed using a different radix. That's hilarious. Everybody loves the syntactic sugar that makes things easy, until the unexpected point where it makes things very hard.

What is the syntactic sugar here? I mostly see a map that doesn't behave how anyone would expect.

The syntactic sugar here is that map and parseInt can be called without specifying all parameters. (But, since JS has no function overloading, it's the same function you're calling.)
Post reply on HN