map: Learn JavaScript's Array Methods by Building Them
Next in the series exploring JavaScript's array methods - map. Perhaps one of the most commonly used and powerful of JavaScript's array methods.
December 3, 2020 | 6 minute readJavaScript’s map
method is a powerful method for working with arrays and one of the most commonly used. You’ll likely see it used all over a React codebase to turn an array of data from an API into an array of components. It’s also just a small step up in complexity from forEach
, which we covered previously.
This article is part of a series of articles where I explain JavaScript’s built-in array methods. This article focuses on the map
method, which is used to create a new array based an existing one. I’ll describe how it works and when to use it, but most of the article will focus on implementing map
for yourself.
How it Works
The map
method takes an existing array and a function and executes the function on each item of the array. This is just like the forEach
method, but there is one key difference between forEach
and map
: the map
method returns a new array based on the result of executing the passed in function. There’s a subtle requirement for the the function passed into map
: it has to return something, so that map
can add it to the array it creates for us.
A technical way of describing map
is: it creates a derivative array based on a function:
const numbers = [1, 2, 3, 4, 5]
function doubleNumber(number) {
return number * 2
}
const doubles = numbers.map(doubleNumber)
console.log(doubles) // [2, 4, 6, 8, 10]
We start with an array of numbers called numbers
and a function called doubleNumber
that takes an argument and returns its double value (the result of multiplying it by 2). By passing doubleNumber
into map
, we’re saying, “invoke this function on each item in the numbers
array,” which results in the array [2, 4, 6, 8, 10]
(or original array, numbers
contains the values [1, 2, 3, 4, 5]
).
It’s important to remember that every array method can be implemented with a for
loop, including map
.
A beginner might approach the task of doubling each item in an array with something that looks like this:
const numbers = [1, 2, 3, 4, 5];
const doubles = []
for (let i = 0; i < numbers.length; i++) {
let number = numbers[i]
let double = number * 2
doubles.push(double)
}
console.log(doubles) // [2, 4, 6, 8, 10]
We can simplify this a bit by turning the actual “action” we’re performing into it’s own function and bringing back our doubleNumber
function:
const numbers = [1, 2, 3, 4, 5];
const doubles = []
function doubleNumber(number) {
return number * 2
}
for (let i = 0; i < numbers.length; i++) {
let number = numbers[i]
let double = doubleNumber(number)
doubles.push(double)
}
console.log(doubles) // [2, 4, 6, 8, 10]
This actually brings us very close to our own implementation of map
.
Implementing Our Own
We said earlier that map
is used to create a derivative array:
const numbers = [1, 2, 3, 4, 5]
function doubleNumber(number) {
return number * 2
}
const doubles = numbers.map(doubleNumber)
console.log(doubles) // [2, 4, 6, 8, 10]
This code snippet from before starts with an array, numbers
and uses map
and the function doubleNumbers
to create the doubles
array.
Based on how we’re using it here, we can determine that the requirements for the map
method are:
- A function that takes an array and a function
- Invokes the passed in function on each item in the passed in array
- Stores the result of the function call in a new array
- Returns the new array
Starting with the first requirement, we can create a function called map
that takes an array and a function as arguments:
function map(arr, fn) {
// more to come here
}
Then, we want to loop through the array, using basically the same loop we used above, so that we can invoke the passed in function on each item in the next step:
function map(arr, fn) {
for (let i = 0; i < arr.length; i++) {
// more to come here
}
}
Now we pass each item in the array into fn
:
function map(arr, fn) {
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
let newItem = fn(item)
}
}
We’ve got the new value from our function. Based on the requirements, we need to store it into an array and return that array:
function map(arr, fn) {
const final = []
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
let newItem = fn(item)
final.push(newItem)
}
return final
}
That’s all there is to it!
Doubling each item in an array with our map
function would now look like this:
const numbers = [1, 2, 3, 4, 5, 6];
const doubles = map(numbers, doubleNumber)
console.log(doubles) // [2, 4, 6, 8, 10, 12]
Just like with forEach
, this is much easier to read and understand than writing out the full for
loop.
map
in Action
Now that we’ve implemented map
for ourselves, we can explore its use a little bit more. I typically use map
for one of two things: transforming an array of data or extracting data from an existing array.
I’ll walk you through examples of both using the following data:
const profiles = [
{
name: "Mercedes",
profession: "Software Engineer"
},
{
name: "Antonio",
profession: "Front-end Developer"
},
{
name: "Ana",
profession: "Back-end Developer"
}
]
This array contains objects representing engineers where each engineer’s profile contains their name and their profession.
Transforming Data
The doubles
example from above is a simple example of using map
to transform data. Our data typically isn’t that simple though. So a more complex example would be to take our profiles
array and create a new array of descriptions for each engineer.
const descriptions = map(profiles, function createDescription(profile) {
let {name, profession} = profile
return `${name} is a ${profession}`
})
console.log(descriptions) // [ 'Mercedes is a Software Engineer', 'Antonio is a Front-end Developer', 'Ana is a Back-end Developer' ]
In the above snippet of code, we take our array of profiles and map
over it with the createDescription
function, which takes a profile and returns a string "<name> is a <profession>"
. Now we have an array of profiles and a separate array with each engineer’s description.
We can modify this slightly to instead give us a new array of profiles where each profile includes the description. This is often even more helpful:
map(profiles, function createDescription(profile) {
let {name, profession} = profile
return {
...profile,
description: `${name} is a ${profession}`
}
})
This code would produce an array that looks like this:
[
{
name: 'Mercedes',
profession: 'Software Engineer',
description: 'Mercedes is a Software Engineer'
},
{
name: 'Antonio',
profession: 'Front-end Developer',
description: 'Antonio is a Front-end Developer'
},
{
name: 'Ana',
profession: 'Back-end Developer',
description: 'Ana is a Back-end Developer'
}
]
Extracting Data
Another common way to use map
is to extract data into a separate array. For instance, we can take our profiles
array and create an array of just the names:
const names = map(profiles, function getProfileNames(profile) {
return profile.name
})
console.log(names)
This is really common when an API gives you a lot of data that you don’t need. I find myself doing something like this a lot when I’m working with charting libraries and data from APIs. The data from the API comes back in the format determined by the API’s schema and I need to extract some and transform it into a format that will work with the charting library.
Conclusion
That concludes my discussion of map
. If you haven’t read the similar discussion of forEach
, then you can find it here. I’m going to go through and explain and implement every built-in array method, which will help you understand how they work and when and how to use them. To follow along, sign up for my newsletter and follow me on Twitter.