---
title: 'Tricky use case of Array.prototype.map in JS'
date: '2020-09-22'
tags: ['javascript', 'array', 'map', 'functional-programming', 'use-cases']
summary: "If you are familiar with functional programming, Array.prototype.map must be a function that you work with every day. We encountered a tricky use case of it at Cốc Cốc recently, it took me a while to figure out, and here's the answer..."
images: ['/static/images/banners/boy-playing-jenga__mparzuchowski__geNNFqfvw48.jpeg']
authors: ['default']
---

If you are familiar with [functional programming](https://en.wikipedia.org/wiki/Functional_programming), **Array.prototype.map** must be a **function** that you work with every day.

For me, **map()** is a powerful function, but there might be some tricky use case of it that you don't know about!

Let's walk through some basic knowledge

> The **map()** method creates a new array from the calling array by applying a provided **callback function** once for each element of the calling array

## Simple use cases

Giving this array

```js showLineNumbers
let devs = [
  { id: '1', name: 'Leo', yob: 1995 },
  { id: '2', name: 'Paul', yob: 1995 },
  { id: '3', name: 'Jesse', yob: 1996 },
  { id: '4', name: 'Ken', yob: 1997 },
]
```

What can we do using **map()** function:

- Getting new values from array

```js showLineNumbers
let ages = devs.map((dev) => {
  let currentYear = new Date().getFullYear()
  return currentYear - dev.yob
})

console.log(ages) // => [25, 25, 24, 23]
```

- Extracting values from array of objects

```js showLineNumbers
let names = devs.map((dev) => dev.name)

console.log(names) // => ["Leo", "Paul", "Jesse", "Ken"]
```

- Rendering list in React application

```js showLineNumbers
import React from 'react'

export default DeveloperProfiles = ({ devs }) => {
  return (
    <ul>
      {devs.map((dev) => (
        <li key={dev.id}>{dev.name}</li>
      ))}
    </ul>
  )
}
```

## Tricky use case

It is common to pass the pre-defined **callback** function as **map()** argument like this:

```js showLineNumbers
let extractId = (dev) => {
  return dev.id
}

let ids = devs.map(extractId)

console.log(ids) // => ["1", "2", "3", "4"]
```

But this habit may lead to unexpected behaviors with **functions** that take additional **arguments**.

Consider this case - we need to get all **ids** as **numbers**:

```js showLineNumbers
// ids = ["1", "2", "3", "4"]
let idsInNumber = ids.map(parseInt)

console.log(idsInNumber) // => ???
```

You might expect the result is `[1, 2, 3, 4]`, but the actual result is `[1, NaN, NaN, NaN]` <Twemoji emoji="astonished-face" />

We encountered this problem at [Cốc Cốc](https://coccoc.com/) recently, it took me a while to figure out, and here's the answer...

Usually, we use **map()** callback with 1 **argument** (the element being traversed), but **Array.prototype.map** passes 3 arguments:

- the **element**
- the **index**
- the **calling array** (which we don't use most of the time)

And the callback in this case - [parseInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) is often used with 1 **argument** but it takes 2:

```js showLineNumbers
// Syntax
parseInt(string [, radix])
```

- `string`: the value to parse
- `radix`: **[Optional]**: an integer that represent the **radix** (the base in mathematical numeral systems) - usually, it's **10**

For example:

```js showLineNumbers
parseInt('10') // => 10
parseInt('10', 10) // => 10
parseInt('10', 2) // => 2
parseInt('10', 4) // => 4
```

And here you can see the source of confusion <Twemoji emoji="eyes" />!

The third **argument** of **map()** is ignored by **parseInt** - but **_not_** the second one!

Hence, the **iteration** steps of **map()** look like this:

```js showLineNumbers
// map(parseInt) => map(parseInt(value, index))

/* index is 0 */ parseInt('1', 0) // => 1
/* index is 1 */ parseInt('2', 1) // => NaN
/* index is 2 */ parseInt('3', 2) // => NaN
/* index is 3 */ parseInt('4', 3) // => NaN
```

## Solution

As you've known the source of the problem, the solution is not to pass all of the **map()**'s arguments to your **callback** if you're not sure how it works

- Passing only the arguments that your callback needs

```js showLineNumbers
function returnInt(element) {
  return parseInt(element, 10)
}

;['1', '2', '3', '4'].map(returnInt)
// => [1, 2, 3, 4]

// Same as above, but using the concise arrow function syntax
// Better use this if you don't need to re-use the callback
;['1', '2', '3', '4'].map((numb) => parseInt(numb, 10))
// => [1, 2, 3, 4]
```

- A simpler way to achieve our case like [Airbnb style guide](https://github.com/airbnb/javascript#coercion--numbers)

```js showLineNumbers
;['1', '2', '3', '4'].map(Number)
// => [1, 2, 3, 4]
```

And that's what I know about **Array.prototype.map** function, feel free to share your use cases in the comment section <Twemoji emoji="backhand-index-pointing-down" />

Happy coding!

## Refs

- [Array.prototype.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
- [**parseInt** is a bad guy](https://github.com/denysdovhan/wtfjs#parseint-is-a-bad-guy)
- [A JavaScript Optional Argument Hazard](http://www.wirfs-brock.com/allen/posts/166)
- [https://codesource.io/use-cases-of-array-map-you-should-know](https://codesource.io/use-cases-of-array-map-you-should-know)
- [https://github.com/airbnb/javascript#coercion--numbers](https://github.com/airbnb/javascript#coercion--numbers)
