Live data from Hacker News

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

medium.com

91–100 of 163 posts

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

#91

Earlier quoted context omitted.

The surprising bit comes in with the fact that: 1. parseInt is used with only a single argument in most cases. 2. Only the first argument of map is used in most cases. Using multiple parameters for those are so uncommon that it's easy not to realise that it's supported. _If you know all of that_ it may not be surprising to you, but in most cases there is no need for the average developer to know it, making it very su…

Why would you use parseInt over Math.round if you only expect a single arg? Seems like you'd only want to use parseInt if you expect to need radix changes at some point, e.g. converting between hex strings, decimal values, and binary strings ['1', '7', '11'].map(Math.round) // => [1, 7, 11] [["00000001", 2], ["00000111", 2], ["0x0B", 16]].map(x => parseInt(...x)) // => [1, 7, 11]

If you want to get terse

    ['1', '7', '11'].map(x => +x)

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

#92
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, this gotcha doesn't bother me nearly as much as the classic:

  [9, 10, 11].sort()

  [ 10, 11, 9 ]

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

#93
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.

It may be logical but:

- I know all that, have been programming for 15 years, yet I still would have done the mistake.

- Simple unit tests may very well not catch this bug the first time.

- The language design allows your brain to ignore the index parameter because JS accepts superfluous parameters, which is a terrible decision.

- map() is a mapping primitive. It's supposed to adapt a type to another type so that you can pass it to a monoid. JS breaks this convention.

- Even traditionally not functional languages know better than that and separate iteration from indexing. E.G: python map() just maps, and if you want numbering, you use explicitly enumerate(). Ruby has each() and each_with_index(), etc.

Just another of the numerous sucky things in JS. They are not big, but they accumulate very quickly and make it one of the worse language existing. Certainly the worst of all modern stack languages. It's a shame it has a monopoly on the most awesome platform in the world: the web.

In fact, it's such a big problem the most popular JS projects are all things to avoid coding in JS or workaround JS deficiencies: typescript, coffeescript, jsx, babel, webpack, lowdash ...

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

#94
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").

this is a relic from issues in ie8, and you no longer need to do this if you're not supporting ie8. hell even if you use ie8 in almost 99% of cases you won't need to bother.

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

#95
post #51

Earlier quoted context omitted.

The surprising bit comes in with the fact that: 1. parseInt is used with only a single argument in most cases. 2. Only the first argument of map is used in most cases. Using multiple parameters for those are so uncommon that it's easy not to realise that it's supported. _If you know all of that_ it may not be surprising to you, but in most cases there is no need for the average developer to know it, making it very su…

There's some interesting history for awareness of the first issue: parseInt used to be a notorious source of bugs and so it used to be more common to see it with a second argument on any project which used a linter in that era. ES5 deprecated that behaviour and it's been widespread enough that code which started in the IE9+ era now looks like the very old buggy code omitting the radix, so anyone getting started relat…

I didn't know about the second parameter until I saw it in a TypeScript signature a few months ago, which acted as a sort of warning to me to avoid using it incorrectly.

But TypeScript doesn't catch this specific bug because it's technically correct[1] from an API stand-point, as the second parameter to parseInt is a number and the second argument to map's callback is a number.

For as many situations as TypeScript genuinely helps you, there are just as many situations where it gives you false security, like in this case, and I'm starting to consider not using it anymore. [2]

[1] not actually "the best kind of correct" despite popular television quotes

[2] https://sdegutis.com/2019-06-20-considering-removing-typescr...

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

#96
This article made me curious to why a Base-1 or Unary Numeral base system has not been implemented in parseInt() as noted by the OP, since the second argument radix must be between 2 and 36:

    parseInt('1', 1)   // same error for '0' or '|'
    > NaN
The result is intriguing still as "NaN" seems to indicate a invalid input in the first parameter instead of a invalid second parameter.

I've found that apparently there is no consensus [1] on Base-1 notation or parsing, although my primary intuition is correct [2] in that a parser could be written that would parse a "1" as a 1 base10, "11" as a 2 base10, "111" and so on. The parser would probably look a lot like a simple length() function, but that could vary with certain base-1 encodings like the ones used by the Golomb Rice compression algorithms, which have each string end in "0" (unary coding).

[1] https://math.stackexchange.com/questions/371972/what-would-b...

[2] https://en.wikipedia.org/wiki/Unary_numeral_system

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

#97
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.

It may be logical but: - I know all that, have been programming for 15 years, yet I still would have done the mistake. - Simple unit tests may very well not catch this bug the first time. - The language design allows your brain to ignore the index parameter because JS accepts superfluous parameters, which is a terrible decision. - map() is a mapping primitive. It's supposed to adapt a type to another type so that you…

> - The language design allows your brain to ignore the index parameter because JS accepts superfluous parameters, which is a terrible decision.

Without that we would not be able to have variable length argument lists in the past.

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

#98

Earlier quoted context omitted.

The problem is that this article is attributing this problem to JS, whereas the problem is with either computer science or human decimal bias: i.e. the radix is not a concept invented by JS. If you think it should be ok for all devs to believe that parseInt === parseDecimalInt then maybe all languages should be decimal-only. That isn't the case though.

All languages I know of _default_ to a radix of 10 though, allowing other radixes where a prefix is added like 0b111010, 0xa7b45fd9, etc. Seems intuitive that if parseInt allows omitting the radix parameter, that it should default to whatever radix a standard integer primitive would default to.

> Seems intuitive that if parseInt allows omitting the radix parameter, that it should default to whatever radix a standard integer primitive would default to.

But in this case, the radix is _not_ omitted. The map function passes the index of the current iteration as the second parameter to the function it is passed. It is kinda like this: ``` ['1','7','11'].map((item, index) => parseInt(item, index))

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

#100
post #91

Earlier quoted context omitted.

Why would you use parseInt over Math.round if you only expect a single arg? Seems like you'd only want to use parseInt if you expect to need radix changes at some point, e.g. converting between hex strings, decimal values, and binary strings ['1', '7', '11'].map(Math.round) // => [1, 7, 11] [["00000001", 2], ["00000111", 2], ["0x0B", 16]].map(x => parseInt(...x)) // => [1, 7, 11]

If you want to get terse ['1', '7', '11'].map(x => +x)

This coerces to a number, but not to an integer.
Post reply on HN