Клавиша / esc

.map()

Собирает новый массив из запчастей старого.

Время чтения: 5 мин

Кратко

Скопировано

Метод map() позволяет трансформировать один массив в другой при помощи функций-колбэка. Переданная функция будет вызвана для каждого элемента массива по порядку. Из результатов вызова функции будет собран новый массив.

Пример

Скопировано

Создадим массив квадратов:

        
          
          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]

        
        
          
        
      

Либо можно сделать массив с объектами:

        
          
          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 }
// ]

        
        
          
        
      

Интерактивный пример:

Открыть демо в новой вкладке

Как пишется

Скопировано

Как и другим похожим методам, map() необходимо передать колбэк-функцию, которая будет возвращать какое-то значение. Именно это значение попадёт в итоговый трансформированный массив.

Функция, которую мы передаём в метод map(), может принимать три параметра:

  • item — элемент массива в текущей итерации;
  • index — индекс текущего элемента;
  • arr — сам массив, который мы перебираем.

Суммируем числа, индексы и длины массива:

        
          
          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]

        
        
          
        
      

Как понять

Скопировано

Часто возникают ситуации, когда на основе одного массива нужно создать другой массив, как-нибудь трансформировав исходные значения. Это можно сделать, используя обычные циклы for или 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']

        
        
          
        
      

Задача решена, однако, как и другие встроенные методы массива, map() позволяет написать код короче и проще для понимания:

        
          
          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']

        
        
          
        
      

Результат тот же, но способ короче и проще.

Подсказки

Скопировано

💡 map() возвращает новый массив, при этом исходный массив никак не изменится.

💡 При работе с map() необходимо возвращать значение из функции-колбэка. Если не вернуть значение — например, забыв обработать какую-то ветку условия, то в итоговом массиве будет undefined:

        
          
          const nums = [1, 2, 3, 4, 5]const transformed = nums.map(function (num) {  if (num <= 3) {    return "less"  }  // Забыли обработать эту ветку условия})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"
  }
  // Забыли обработать эту ветку условия
})
console.log(transformed)
// ['less', 'less', 'less', undefined, undefined]

        
        
          
        
      

💡 Размер массива, которые возвращает map() всегда совпадает с размером массива, который обходится.

💡 Функция-колбэк будет вызвана только для элементов имеющих установленное значения.

Создадим массив, используя конструктор с указанием размера массива Array(length). Все элементы этого массива являются незаполненными:

        
          
          const newArray = Array(5)console.log("Размер массива:", newArray.length);// Размер массива: 5console.log(newArray)// [<5 empty items>]
          const newArray = Array(5)
console.log("Размер массива:", newArray.length);
// Размер массива: 5
console.log(newArray)
// [<5 empty items>]

        
        
          
        
      

Попытаемся использовать метод map() для установки в качестве значения элемента его индекс. В результате массив initedArray останется таким же пустым как и исходный массив newArray:

        
          
          const initedArray = newArray.map(function (item, index) {    console.log("элемент:", item)    return index})console.log(initedArray)// [<5 empty items>]
          const initedArray = newArray.map(function (item, index) {
    console.log("элемент:", item)
    return index
})

console.log(initedArray)
// [<5 empty items>]

        
        
          
        
      

Для решения этой проблемы можно воспользоваться деструктуризацией исходного массива:

        
          
          const newArray = Array(5)console.log("Размер массива:", newArray.length)// Размер массива: 5const initedArray = [ ...newArray ].map(function (item, index) {    console.log("элемент:", item)    return index})// элемент: undefined// элемент: undefined// элемент: undefined// элемент: undefined// элемент: undefinedconsole.log(initedArray)// [ 0, 1, 2, 3, 4 ]
          const newArray = Array(5)
console.log("Размер массива:", newArray.length)
// Размер массива: 5

const initedArray = [ ...newArray ].map(function (item, index) {
    console.log("элемент:", item)
    return index
})
// элемент: undefined
// элемент: undefined
// элемент: undefined
// элемент: undefined
// элемент: undefined

console.log(initedArray)
// [ 0, 1, 2, 3, 4 ]

        
        
          
        
      

💡 Для наполнения массива одинаковыми значениями можно воспользоваться рецептом Создание массива из большого количества повторяющихся элементов.

Передача контекста в колбэк-функцию

Скопировано

Вторым аргументом map() может принимать значение, которое будет передано как контекст выполнения функции-колбэка:

        
          
          const nums = [1, 2, 3]const otherData = { delta: 5 }const transformed = nums.map(function (num) {  // this теперь ссылается на объект 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 теперь ссылается на объект 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((num) => {  // this.delta в данном случае равен 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 в данном случае равен undefined
  return num + this.delta
}, otherData)

console.log(transformed)
// [ NaN, NaN, NaN ]

        
        
          
        
      

На практике

Скопировано

Егор Огарков советует

Скопировано

🛠 При работе с React или другой похожей на неё библиотекой map() — это самый распространённый способ трансформировать массив данных в компоненты, которые в итоге будут на странице:

        
          
          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>
  )
}

        
        
          
        
      

🛠 Так как map() возвращает массив, то у полученного массива мы можем продолжать по цепочке вызывать другие методы массива, в том числе, новый map(), продолжая трансформацию новых данных:

        
          
          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(...)

        
        
          
        
      

На собеседовании

Скопировано
Задать вопрос в рубрику
🤚 Я знаю ответ

Алекс Рассудихин  отвечает

Скопировано

Методы forEach() и map() определены на нескольких структурах данных. Мы рассмотрим чем отличаются эти методы для массивов.

Оба метода принимают колбэк, который вызывается для каждого элемента. Разница в том, что метод forEach() ничего не возвращает, а метод map() возвращает новый массив с результатами вызова колбэка на каждом исходном элементе. Если переданный колбэк ничего не возвращает в новом массиве появится undefined

Вы можете вернуть значение и из колбэка для forEach() но оно никак не будет использоваться дальше.

        
          
          [1,2,3].forEach(a => a + 3);
          [1,2,3].forEach(a => a + 3);

        
        
          
        
      

Используя map() вы можете создавать цепочки вызовов. Если же вы будете использовать forEach() так сделать не получится.

        
          
          const myArray = [4, 2, 8, 7, 3, 1, 0];const myArray2 = myArray.map(item => item * 2).sort((a, b) => a - b);console.log(myArray); // [4, 2, 8, 7, 3, 1, 0]console.log(myArray2); // [0, 2, 4, 6, 8, 14, 16]
          const myArray = [4, 2, 8, 7, 3, 1, 0];
const myArray2 = myArray.map(item => item * 2).sort((a, b) => a - b);

console.log(myArray); // [4, 2, 8, 7, 3, 1, 0]
console.log(myArray2); // [0, 2, 4, 6, 8, 14, 16]

        
        
          
        
      

В примере выше изменился только myArray2.

Для закрепления, реализуем ту же самую логику при помощи forEach().

        
          
          const myArray = [4, 2, 8, 7, 3, 1, 0];let myArray2 = [];myArray.forEach(item => {  myArray2.push(item * 2);});myArray2 = myArray2.sort((a, b) => a - b);console.log(myArray); // [4, 2, 8, 7, 3, 1, 0]console.log(myArray2); // [0, 2, 4, 6, 8, 14, 16]
          const myArray = [4, 2, 8, 7, 3, 1, 0];
let myArray2 = [];

myArray.forEach(item => {
  myArray2.push(item * 2);
});

myArray2 = myArray2.sort((a, b) => a - b);

console.log(myArray); // [4, 2, 8, 7, 3, 1, 0]
console.log(myArray2); // [0, 2, 4, 6, 8, 14, 16]