# The map(parseInt) Issue and Unary a Function

## 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) {
...
} else {
...
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)
}
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!