Уроки по веб-разработке

map() js — перебор и возвращение массива

Данный метод, в отличии от ранее рассмотренного метода forEach(), вернет нам новый массив, при этом исходный массив не изменяется.

Синтаксис данного метода аналогичен методу forEach

callback — функция, которая будет выполнена для каждого элемента массива.
Callback может быть стрелочной функцией и принимает такие параметры

thisArg — это необязательный параметр. Значение, используемое в качестве this при вызове функции callback.

Давайте рассмотрим простой пример:

const arr = [1, 2, 3];

const newArr = arr.map((item, index) => {
    // увеличиваем значение каждого элемента на 1
    // item - текущий элемент массива
    return item + 1;
});

console.log(newArr); // [2, 3, 4]
console.log(arr); // [1, 2, 3]

В качестве callback можно передать ранее созданную функцию:

const arr = [1, 2, 3];

function doThisForEachElement(item, index) {
    return item + 1;
}

const newArr = arr.map(doThisForEachElement);

console.log(newArr); // [2, 3, 4]

Примеры использования метода map()

Теперь давайте рассмотрим еще несколько примеров.

Пример 1
Что будет если мы попробуем вернуть из колбека элементы по условию? Допустим из предыдущего примера, мы хотим вернуть только числа меньше или равные единице:

const arr = [1, 2, 3];

const newArr = arr.map(item => {
    if (item <= 1) return item;
});

console.log(newArr); // [1, undefined, undefined]

В таком случае мы получим undefined для значений, которые не прошли условие. Если вам нужно вернуть не все значения в новый массив, а только те что удовлетворяют условию — тогда используйте метод filter()

Пример 2
Продолжим с массивами чисел. Допустим, нам нужно вернуть в новый массив числа возведенные в определенную степень. Как делать возведение в степень в JavaScript рассматривали в другом посте.

const arr = [2, 4, 5, 7];

const newArr = arr.map(item => {
    // возведем каждый элемент в третью степень
    return item ** 3;
});

console.log(newArr); // [8, 64, 125, 343]

Также можно сделать упрощенный вариант записи, потому что мы сразу возвращаем измененное значение:

const newArr = arr.map(item => item ** 3);

Пример 3
Метод JS map() используется в React для рендеринга элементов. Вот как пример можете посмотреть код тут

const Todos = (todos) => {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  )
}

У нас есть массив todos и на каждой итерации мы отрисовываем элемент li

Использование метода map() с объектами

Также мы можем использовать метод map() с массивом объектов и с самими объектами.

Пример 4
Допустим у нас есть массив пользователей и нам нужно провести определенные действия с каждым из пользователей — добавим свойство role:

const users = [
    { id: 1, name: 'John', salary: 3000, position: 'developer' },
    { id: 2, name: 'Kate', salary: 2800, position: 'designer' },
    { id: 3, name: 'Bob', salary: 3200, position: 'manager' }
];

const newArray = users.map(item => {
    item.role = 'admin';
    return item;
});

// Будет возвращен новый массив со свойством 'role' 
// для каждого пользователя
console.log(newArray); // { id: 1, ..., role: 'admin' }

В данном случае, если вы выведете в консоль массив users, то заметите что там тоже добавилось свойство role

Если нужно сохранить исходный массив неизменным, тогда нужно создать новый объект для этого и с помощью оператора spread скопировать свойства старого и добавить в него новые свойства:

const newArray = users.map(item => {
    return { ...item, role: 'admin' }
});

// Останется неизменным
console.log(users); 

// Новый массив со свойством 'role'
console.log(newArray);

Также можно задать условие и добавить разные свойства в зависимости от position:

const users = [
    { id: 1, name: 'John', salary: 3000, position: 'developer' },
    { id: 2, name: 'Kate', salary: 2800, position: 'designer' },
    { id: 3, name: 'Bob', salary: 3200, position: 'manager' }
];

const newArray = users.map(item => {
    if (item.position === 'manager') {
        return { ...item, role: 'admin' }
    }
    
    return { ...item, role: 'user' }
});

// Role = admin будет добавлено только 3 пользователю
// У остальных role будет = user 
console.log(newArray);

Или вот такая короткая запись с помощью тернарного оператора:

const newArray = users.map(item => 
    item.position === 'manager' ? { ...item, role: 'admin' } : { ...item, role: 'user' }
);

Своя реализация метода map

let array = [1, 5, 10, 15];

Array.prototype.customMap = function (callback, thisArg) {
    // Проверяем существует ли контекст вызова this (по умолчанию это сам массив)
    if (this == null) {
     throw new Error("It has to be array");
    }
    
    let context = this;
    
    let obj = Object(this);
    
    
    // Проверям кол-во переданных аргументов
    // Если больше 1, значит был передан thisArg
    if (arguments.length > 1) {
     context = thisArg;
    }
    
    // Если callback это не функция, тогда выдаем оишбку
    if (typeof callback !== "function") {
     throw new Error("Callback has to be a function");
    }
    
    // Длинаа переданного масива
    let len = obj.length;
    
    let newArray = [];
    
    let i = 0;
    
    // Для каждого элемента массива вызываем callback
    // И затем добавляем в массив, который в результат вернем
    while (i < len) {
     if (i in obj) {
       newArray[i] = callback.call(context, this[i], i, obj);
     }
    
     i++;
    }
    
    return newArray;
};

// Теперь используем свой полифил
const newArray = array.customMap(number => number + 1);

console.log(newArray); // [2, 6, 11, 16]

Когда не следует использовать метод map()

Данный метод не следует использовать в таких случаях:

  1. Если вам нужен просто проход по массиву и вы не будете использовать возвращенный из данного метода массив. В таком случае лучше использовать forEach()
  2. Также когда функция (callback), которую вы передаете в качестве аргумента, не будет делать return
let array = [1, 5, 10, 15];

const newArray = array.map((item) => {
  item * 2; // не возвращается результат
});

console.log(newArray); // [undefined, undefined, undefined, undefined]