# The map(parseInt) Issue and Unary a Function

### Wednesday, July 20, 2016

## Background

Years ago I watched a YouTube video about the bad parts of JavaScript and the author listed a few examples. One of those impressed me:

```
var numbers = ['1', '2', '3'];
numbers.map(parseInt);
> [1, NaN, NaN]
```

## Ploblem

In the first place the example showed how buggy was JavaScript. The `Array.prototype.map`

method calls the provided `parseInt`

function once for each element in the array and converts strings into integers as a result. The expectation therefore was that each string would be parsed into an integer but the actual result failed and made the last two elements `NaN`

.

So why does `parseInt`

regard the last two elements as *Not A Number* s ? Thanks to the hints from my colleague, let’s have a closer look at the code.

## Explanation

What arguments does the `Array.prototype.map`

pass into the `callback`

function? Three. One is the current element being processed, another is the index of the current element in the array and the other is the entire array which `map`

was called upon. It’s those three arguments that the `parseInt`

method accepts (Perhaps now you know what happened under the hook :-)

Every step how `parseInt`

is executed is illustrated as below:

```
parseInt('1', 0, numbers) // S#1
parseInt('2', 1, numbers) // S#2
parseInt('3', 2, numbers) // S#3
```

The third argument `numbers`

actually in every step is ignored and won’t affect the result because `parseInt`

only defines two parameters: *string* and *radix*.

**S#1**: the *string* is `1`

and the *radix* is `0`

. As MDN describes, when the *radix* is 0 or undefined the method falls back to the default behavior and treats the input string as decimal unless the input string starts with a zero (octal) or `0x`

(hexadecimal). So `parseInt('1', 0)`

is equal to `parseInt('1', 10)`

and the result is a decimal `1`

as expected.

- If the input string begins with “0x” or “0X”, radix is 16 (hexadecimal) and the remainder of the string is parsed.
- If the input string begins with “0”, radix is eight (octal) or 10 (decimal). Exactly which radix is chosen is implementation-dependent. ECMAScript 5 specifies that 10 (decimal) is used, but not all browsers support this yet. For this reason always specify a radix when using parseInt.
- If the input string begins with any other value, the radix is 10 (decimal).

**S#2**: in this situation the *radix* is 1 and obviously it is not reasonable so the result is `Not A Number`

as expected.

Here’s how V8 deals with the `1`

radix:

```
function GlobalParseInt(string, radix) {
if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
...
} else {
...
if (!(radix == 0 || (2 <= radix && radix <= 36))) {
return NaN;
}
}
}
```

**S#3**: in this situation the radix `2`

is reasonable but the input string `3`

is not. Binary numbers use only two different symbols: 0 and 1. So the result is `Not A Number`

as expected.

## Conclusion

The unexpected behavior of the code shown above is not caused by JavaScript but by the programmers. There’s a famous saying by an elder in China, “The youth ought to level up their knowledge” :-)

## Solutions

### Lodash

What if `parseInt`

only accepts one argument and ignores others? Then the problem described above won’t bother us any more.

`lodash`

implements the `unary`

method which creates a function that accepts up to one argument. So the problem can be resolved in a lodash-ized way:

```
numbers.map(_.unary(parseInt))
// OR
_.map(numbers, _.unary(parseInt))
```

### Vanilla JS

What if we use only Vanilla JavaScript? Then we can implement our own `unary`

function. This is my version:

```
function unary (fn) {
function wrapper () {
return fn(arguments[0])
}
return wrapper
}
numbers.map(unary(parseInt))
```

Later I found that lodash implements another function `ary`

to slice the number of arguments which the target function can be invoked with and `lodash#unary`

calls it under the hook. So I refactor the original version with an `ary`

-like function:

```
function ary (fn, n) {
const argsLength = n ? n : fn.length
function wrapper () {
return fn.apply(this, Array.from(arguments).slice(0, argsLength)
}
return wrapper
}
function unary (fn) {
return ary(fn, 1)
}
numbers.map(unary(parseInt))
```

Those solutions are a bit complicated and we can fix the problem in a much easier way:

```
numbers.map(str => parseInt(str, 10))
```

and done!