The Array.map Method in JavaScript

Published: Thursday, May 26, 2022
Updated: Friday, June 3, 2022

Greetings, friends! Three of the most useful functions in JavaScript are the Array.map, Array.filter, and Array.reduce methods. Each of these methods is known as a higher-order function that accept a callback function.

Array.forEach versus Array.map

In this tutorial, I will discuss the Array.map method. Before we continue, I highly recommend checking out my article on the Array.forEach method. The map method is very similar to the forEach except for one key difference. The map method will return an array, but the forEach method will return undefined.

You will typically use the forEach method to simply execute a callback function for each element in an array. It's best practice to not modify the original array using the forEach method. If you want to perform any transformations to elements of an array or return a new array based on elements of the original array, then it's better to use the map method.

Let's look at an example of the forEach method. It will execute a callback function for each element in array.

javascript
Copied! ⭐️
const array = [1, 2, 3];

array.forEach(num => {
  console.log(num); // num stands for "number"
});

/* OUTPUT:
1
2
3
*/

Next, let's look at an example of the map method. It will return a new array containing elements transformed by the callback function.

javascript
Copied! ⭐️
const array = [1, 2, 3];

const newArray = array.map(num => {
  return num * 2;
});

console.log(newArray);
// OUTPUT: [2, 4, 6]

It's important to note that the map method returns a NEW array. It does not modify the original array. It's considered best practice not to modify the original array from within the callback function passed into the map method.

Let's look at the Array.map method's documentation more closely. According to MDN, this method can accept two parameters and returns a new array with each element being the result of the callback function.

javascript
Copied! ⭐️
map(callbackFn, thisArg)

Notice that this function definition is very similar to the Array.forEach method.

The first parameter - callbackFn

The first parameter we pass into the map method is a callback function, and the second parameter is thisArg, a value to use as the this context when executing callbackFn. It's really rare to use the second parameter. It's also an optional parameter. Let's just look at the first parameter, the callback function. We'll discuss the second parameter later.

The Array.map method expects a callback function that has one, two, or three parameters. The second and third parameters are optional.

javascript
Copied! ⭐️
map((element) => { /* ... */ })
map((element, index) => { /* ... */ })
map((element, index, array) => { /* ... */ })

Using a Callback Function with One Parameter

Let's use the Array.map method with a callback function that has one parameter.

javascript
Copied! ⭐️
const array = [1, 2, 3];

const newArray = array.map(num => {
  return num * 2;
})

console.log(newArray);
// OUTPUT: [2, 4, 6]

The code snippet above passes in an arrow function as a callback function. We could have just as easily defined our own function and pass that into the map method instead.

javascript
Copied! ⭐️
const array = [1, 2, 3];

function doubleNumbers(num) {
  return num * 2;
}

const newArray = array.map(doubleNumbers);
console.log(newArray);
// OUTPUT: [2, 4, 6]

Using a Callback Function with Two Parameters

Let's now take a look at using the map method with a callback function that has two parameters. The second parameter will be the index of the element of the array.

javascript
Copied! ⭐️
const array = ['apples', 'oranges', 'bananas'];

const newArray = array.map((fruit, index) => {
  return {[fruit]: index};
})

console.log(newArray);
/* OUTPUT:
[
  { apples: 0 },
  { orange: 1 },
  { bananas: 2 }
]
*/

In the code snippet above, we are creating a new transformed array using each element of array and the index of each element in the array. The square brackets around [fruit] lets us use a computed property name. This lets us create a dynamic name for the key inside a JavaScript object. The key will change based on the element in the array. The final result, newArray will be an array of objects where each key is equal to an element from array.

Again, we can define a function and pass that into map as a callback:

javascript
Copied! ⭐️
const array = ['apples', 'oranges', 'bananas'];

function getNewTransformedArray(fruit, index) {
  return {[fruit]: index};
}

const newArray = array.map(getNewTransformedArray);

console.log(newArray);
/* OUTPUT:
[
  { apples: 0 },
  { orange: 1 },
  { bananas: 2 }
]
*/

Using a Callback Function with Three Parameters

Next, let's try using a callback with three parameters. The third parameter will be the array we're calling the map method on.

javascript
Copied! ⭐️
const newArray = [0, 0, 0].map((num, index, thisArray) => {
  return thisArray.map((num2, index2) => {
    return (index + 1) * (index2 + 1);
  });
});

console.log(newArray);
/* OUTPUT:
[
  [ 1, 2, 3 ],
  [ 2, 4, 6 ],
  [ 3, 6, 9 ]
]
*/

In the code snippet above, we create a matrix by calling nested map functions. The parameter, thisArray, refers to a reference to the array we're calling the map function on. The array of zeroes has a length of three. We can make a 3x3 matrix by returning an array for each element in the original array. Each value in the matrix will be equal to (index + 1) * (index2 + 1). This example may seem strange, but it was just my attempt at showing how you could utilize the third parameter of the callback function passed into the map method 😅.

You can also shorten the code snippet above by using the concise version of arrow functions.

javascript
Copied! ⭐️
const newArray = [0, 0, 0].map((num, index, thisArray) =>
  thisArray.map((num2, index2) => (index + 1) * (index2 + 1))
);

console.log(newArray);
/* OUTPUT:
[
  [ 1, 2, 3 ],
  [ 2, 4, 6 ],
  [ 3, 6, 9 ]
]
*/

The second parameter - thisArg

We have now seen how to use the map method with a callback function with one, two, and three parameters. However, the callback function was only the first parameter we can pass into the map method. It also accepts a second parameter, thisArg, which is optional.

As mentioned previously, it's rare to use this second parameter. You'll typically see it when people are implementing a complex feature or trying to support old legacy code. Let's see an example of how we might use the thisArg parameter in the map method.

javascript
Copied! ⭐️
function Player() {
  this.spells = [];
  this.count = 0;
  this.hasHeal = false;
}

Player.prototype.add = function (array) {
  this.spells = array.map((element) => {
    this.count++;
    if (element === 'heal') this.hasHeal = true;
    return element + ' ' + 'v2';
  }, this);
};

const nate = new Player();
nate.add(['fireball', 'thunderbolt']);
console.log(nate.spells); // [ 'fireball v2', 'thunderbolt v2' ]
console.log(nate.count); // 2
console.log(nate.hasHeal); // false

const sam = new Player();
sam.add(['blizzard', 'bubble beam', 'heal']);
console.log(sam.spells); // [ 'blizzard v2', 'bubble beam v2', 'heal v2' ]
console.log(sam.count); // 3
console.log(sam.hasHeal); // true
warning
Arrow functions do not bind their own this context and will use their parent scope instead. Assigning an arrow function to Player.prototype.add will cause an error.

In the above code snippet, we are creating a Player object by calling a function. Before JavaScript introduced the concept of classes, it was common to make objects using functions and assigning properties to them using the this keyword.

Since JavaScript is a prototypal language, we can add methods to the player's prototype. Within the Player.prototype.add method, we are calling map and passing a value of this as the second parameter. In this context, the this keyword refers to the Player prototype. This allows us to add update properties on instances of our Player object. In our example, we have two instances: nate and sam.

When we run nate.add(['fireball', 'thunderbolt']), the player, "nate," will have each "spell" added to the list of spells but with v2 (version 2) appended at the end. The spell count will be updated to match the total number of elements in the array. If one of the spells is called "heal," then the hasHeal property will be set to true.

The above code snippet may not seem like a clean approach, but it could be considered performant, since we only have to iterate through the array once to update multiple properties on a single Player object. However, there are much cleaner ways to write this code.

Conclusion

This has been a comprehensive guide to the map method in JavaScript. In summary, the map method accepts similar parameters as the forEach method. However, the map method returns a new array and is more suitable for returning a transformed version of the original array. The map method accepts two parameters: a callback and a value to use as the this context when executing the callback. The callback function can accept three parameters: the current element being processed, the index of that element in the array, and the array map was called upon.

The map method is a great way to create new transformed arrays based on elements of an existing array. It's commonly used in React to create a list of JSX elements and is a very powerful tool to have in your JavaScript tool belt.

Resources