Briefly
The map
method allows transforming one array into another using callback functions. The passed function will be called for each element of the array in order. A new array will be collected from the results of the function calls.
Example
Let's create an array of squares:
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]const squares = nums.map(function (num) { return num * num})console.log(squares)// [1, 4, 9, 16, 25, 36, 49, 64, 81]
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9] const squares = nums.map(function (num) { return num * num }) console.log(squares) // [1, 4, 9, 16, 25, 36, 49, 64, 81]
Or we can create an array of objects:
const objects = nums.map(function (num) { return { field: num, }})console.log(objects)// [// { field: 1 },// { field: 2 },// ...// { field: 9 }// ]
const objects = nums.map(function (num) { return { field: num, } }) console.log(objects) // [ // { field: 1 }, // { field: 2 }, // ... // { field: 9 } // ]
Interactive example:
How it works
Like other similar methods, map
requires a callback function that will return some value. This value will go into the final transformed array.
The function we pass to the map
method can accept three parameters:
item
— the element of the array in the current iteration;index
— the index of the current element;arr
— the array itself that we are iterating over.
Let's sum the numbers, indices, and lengths of the array:
const nums = [0, 1, 2, 3]const transformed = nums.map(function (num, index, arr) { return num + index + arr.length})console.log(transformed)// [4, 6, 8, 10]
const nums = [0, 1, 2, 3] const transformed = nums.map(function (num, index, arr) { return num + index + arr.length }) console.log(transformed) // [4, 6, 8, 10]
How to understand
There are often situations where you need to create one array based on another array, somehow transforming the original values. This can be done using regular loops for
or while
:
const nums = [1, 2, 3, 4, 5]const transformed = []for (let i = 0; i < nums.length; i++) { const num = nums[i] const item = `${i}-${num}` transformed.push(item)}console.log(transformed)// ['0-1', '1-2', '2-3', '3-4', '4-5']
const nums = [1, 2, 3, 4, 5] const transformed = [] for (let i = 0; i < nums.length; i++) { const num = nums[i] const item = `${i}-${num}` transformed.push(item) } console.log(transformed) // ['0-1', '1-2', '2-3', '3-4', '4-5']
The task is solved; however, like other built-in array methods, map
allows you to write code shorter and easier to understand:
const nums = [1, 2, 3, 4, 5]const transformed = nums.map(function (num, i) { return `${i}-${num}`})console.log(transformed)// ['0-1', '1-2', '2-3', '3-4', '4-5']
const nums = [1, 2, 3, 4, 5] const transformed = nums.map(function (num, i) { return `${i}-${num}` }) console.log(transformed) // ['0-1', '1-2', '2-3', '3-4', '4-5']
The result is the same, but the way is shorter and simpler.
Tips
💡 map
returns a new array, while the original array will not be changed.
💡 When working with map
, it is necessary to return a value from the callback function. If you do not return a value — for example, forgetting to handle some branch of the condition, the final array will contain undefined
:
const nums = [1, 2, 3, 4, 5]const transformed = nums.map(function (num) { if (num <= 3) { return "less" } // Forgot to handle this branch of the condition})console.log(transformed)// ['less', 'less', 'less', undefined, undefined]
const nums = [1, 2, 3, 4, 5] const transformed = nums.map(function (num) { if (num <= 3) { return "less" } // Forgot to handle this branch of the condition }) console.log(transformed) // ['less', 'less', 'less', undefined, undefined]
💡 The size of the array returned by map
always matches the size of the array being traversed.
💡 The callback function will be called only for elements with established values.
Let's create an array using the constructor by specifying the size of the array Array
. All elements of this array are unfilled:
const newArray = Array(5)console.log("Array size:", newArray.length);// Array size: 5console.log(newArray)// [<5 empty items>]
const newArray = Array(5) console.log("Array size:", newArray.length); // Array size: 5 console.log(newArray) // [<5 empty items>]
Let's try to use the map
method to set the index as the value of the element. As a result, the inited
will remain as empty as the original new
:
const initedArray = newArray.map(function (item, index) { console.log("element:", item) return index})console.log(initedArray)// [<5 empty items>]
const initedArray = newArray.map(function (item, index) { console.log("element:", item) return index }) console.log(initedArray) // [<5 empty items>]
To solve this problem, we can use destructuring of the original array:
const newArray = Array(5)console.log("Array size:", newArray.length)// Array size: 5const initedArray = [ ...newArray ].map(function (item, index) { console.log("element:", item) return index})// element: undefined// element: undefined// element: undefined// element: undefined// element: undefinedconsole.log(initedArray)// [ 0, 1, 2, 3, 4 ]
const newArray = Array(5) console.log("Array size:", newArray.length) // Array size: 5 const initedArray = [ ...newArray ].map(function (item, index) { console.log("element:", item) return index }) // element: undefined // element: undefined // element: undefined // element: undefined // element: undefined console.log(initedArray) // [ 0, 1, 2, 3, 4 ]
💡 To fill an array with the same values, you can use the recipe Creating an array from a large number of repeating elements.
Passing context to the callback function
The second argument of map
can accept a value that will be passed as the execution context of the callback function:
const nums = [1, 2, 3]const otherData = { delta: 5 }const transformed = nums.map(function (num) { // this now refers to the object otherData return num + this.delta}, otherData)console.log(transformed)// [ 6, 7, 8 ]
const nums = [1, 2, 3] const otherData = { delta: 5 } const transformed = nums.map(function (num) { // this now refers to the object otherData return num + this.delta }, otherData) console.log(transformed) // [ 6, 7, 8 ]
Note that arrow functions cannot change the execution context, so passing a second argument will have no effect:
const nums = [1, 2, 3]const otherData = { delta: 5 }const transformed = nums.map((num) => { // this.delta in this case equals undefined return num + this.delta}, otherData)console.log(transformed)// [ NaN, NaN, NaN ]
const nums = [1, 2, 3] const otherData = { delta: 5 } const transformed = nums.map((num) => { // this.delta in this case equals undefined return num + this.delta }, otherData) console.log(transformed) // [ NaN, NaN, NaN ]
In practice
Advice 1
🛠 When working with React or another library similar to it, map
is the most common way to transform an array of data into components that will ultimately appear on the page:
function SomeComponent() { const items = ['This', 'is', 'map!'] return ( <div> {items.map(item => <span>{item}</span>)} </div> )}
function SomeComponent() { const items = ['This', 'is', 'map!'] return ( <div> {items.map(item => <span>{item}</span>)} </div> ) }
🛠 Since map
returns an array, we can continue to chain other array methods on the resulting array, including a new map
, continuing to transform new data:
const nums = [1, 2, 3, 4, 5]const result = nums.map(...).filter(...).map(...)
const nums = [1, 2, 3, 4, 5] const result = nums.map(...).filter(...).map(...)