Mapping (отображение) в JavaScript: преобразование коллекций

Раздел: JavaScript -> Коллекции данных

Основы mapping в JavaScript

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

Метод Array.prototype.map() - наиболее эффективный и идиоматичный способ отображения массива. Он создаёт новый массив, вызывая переданную функцию для каждого элемента исходного массива в порядке их следования. Синтаксис: array.map(callback(currentValue, index, array), thisArg).

const numbers = [1, 2, 3, 4];
const squares = numbers.map(x => x * x);
console.log(squares); // [1, 4, 9, 16]

Js mapping (mapping (отображение) в javascript)

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

Типичная ошибка - ожидать, что map() вернёт изменённый исходный массив. На самом деле возвращается новый массив. Если забыть присвоить результат, преобразование будет потеряно.

Как преобразовать массив без использования встроенных методов?

Классический цикл for даёт полный контроль над процессом.

const arr = [10, 20, 30];
const doubled = [];
for (let i = 0; i < arr.length; i++) {
  doubled.push(arr[i] * 2);
}
console.log(doubled); // [20, 40, 60]

Цель: когда нужна максимальная производительность или необходимо прервать выполнение (чего не умеет map).

Ошибка: случайное использование var вместо let может привести к проблемам области видимости. Также легко допустить опечатку в индексе или длине массива.

Как выполнить действие над каждым элементом и собрать результат в новый массив?

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

const items = ['a', 'b', 'c'];
const uppercased = [];
items.forEach(item => uppercased.push(item.toUpperCase()));
console.log(uppercased); // ['A', 'B', 'C']

Цель: когда нужно выполнить побочный эффект (например, запись в базу данных) и одновременно собрать новые значения. Однако для чистого отображения предпочтительнее map().

Распространённая ошибка - считать, что forEach возвращает массив. На самом деле он возвращает undefined. Поэтому присваивание результата бесполезно.

Как преобразовать массив, используя аккумулятор?

Метод reduce() позволяет накапливать значения. Хотя он не предназначен исключительно для отображения, его можно использовать для создания нового массива на основе исходного.

const nums = [1, 2, 3];
const incremented = nums.reduce((acc, curr) => {
  acc.push(curr + 1);
  return acc;
}, []);
console.log(incremented); // [2, 3, 4]

Цель: когда отображение сочетается с дополнительной логикой (например, фильтрация или группировка). Для простого отображения map() более лаконичен.

Ошибка: забыть вернуть аккумулятор (return acc) - функция вернёт undefined, и reduce упадёт с ошибкой.

Как отобразить свойства объекта в массив?

Для отображения объектов используют Object.keys(), Object.values() или Object.entries() в комбинации с map().

const person = { name: 'Anna', age: 25, city: 'Moscow' };
const descriptions = Object.entries(person).map(([key, value]) => `${key}: ${value}`);
console.log(descriptions); // ['name: Anna', 'age: 25', 'city: Moscow']

Цель: преобразование объекта в массив строк или других структур для последующей обработки.

Ошибка: путать порядок ключей (не гарантируется в старых браузерах) или использовать for...in без проверки hasOwnProperty, что может захватить унаследованные свойства.

Как преобразовать коллекцию Map в массив значений?

Коллекция Map также поддерживает отображение через свой метод map()? На самом деле у Map нет метода map(), но можно получить массив ключей или значений и применить к ним map().

const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
const doubledValues = [...map.values()].map(v => v * 2);
console.log(doubledValues); // [2, 4, 6]

Цель: работа с Map как с источником данных для линейных преобразований. Для отображения пар ключ-значение можно сначала привести Map к массиву записей.

Ошибка: пытаться вызвать map. map() - такого метода нет. Только преобразование в массив.

Расширенные примеры mapping

Использование индекса и thisArg

Пример
const numbers = [10, 20, 30];
const indexed = numbers.map(function(num, i) {
  return this.prefix + num + ' (индекс ' + i + ')';
}, { prefix: 'Число: ' });
console.log(indexed);
// ['Число: 10 (индекс 0)', 'Число: 20 (индекс 1)', 'Число: 30 (индекс 2)']
['Число: 10 (индекс 0)', 'Число: 20 (индекс 1)', 'Число: 30 (индекс 2)']

Цепочка методов: map + filter + reduce

Пример
const transactions = [
  { amount: 100, type: 'income' },
  { amount: 50, type: 'expense' },
  { amount: 200, type: 'income' },
  { amount: 30, type: 'expense' }
];
const totalIncome = transactions
  .filter(t => t.type === 'income')
  .map(t => t.amount)
  .reduce((sum, amt) => sum + amt, 0);
console.log(totalIncome); // 300
300

flatMap - отображение и разворачивание вложенных массивов

Пример
const sentences = ['Hello world', 'JavaScript mapping'];
const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words);
// ['Hello', 'world', 'JavaScript', 'mapping']
['Hello', 'world', 'JavaScript', 'mapping']

Отображение строки как массива символов

Пример
const str = 'Map';
const codes = Array.from(str).map(char => char.charCodeAt(0));
console.log(codes); // [77, 97, 112]
[77, 97, 112]

Преобразование ключей объекта с помощью Object.keys() и map()

Пример
const user = { firstName: 'John', lastName: 'Doe' };
const uppercasedKeys = Object.keys(user).map(k => k.toUpperCase());
console.log(uppercasedKeys); // ['FIRSTNAME', 'LASTNAME']
['FIRSTNAME', 'LASTNAME']

Async map (осторожно: возвращает массив промисов)

Пример
const asyncSquare = async (n) => n * n;
const numbers = [1, 2, 3];
const promises = numbers.map(asyncSquare);
console.log(promises); // [Promise, Promise, Promise]
// Чтобы получить результаты, используйте Promise.all
Promise.all(promises).then(results => console.log(results)); // [1, 4, 9]
[Promise, Promise, Promise]
[1, 4, 9]

Отображение Map в массив объектов

Пример
const map = new Map([
  ['model', 'Tesla'],
  ['year', 2024],
  ['color', 'red']
]);
const entries = Array.from(map, ([key, value]) => ({ key, value }));
console.log(entries);
// [{ key: 'model', value: 'Tesla' }, { key: 'year', value: 2024 }, { key: 'color', value: 'red' }]
[ { key: 'model', value: 'Tesla' }, { key: 'year', value: 2024 }, { key: 'color', value: 'red' } ]

Использование map с пустыми элементами массива

Пример
const sparseArray = [1, , 3]; // элемент с индексом 1 отсутствует
const result = sparseArray.map(x => x * 2);
console.log(result); // [2, empty, 6] - пустой элемент не вызывается
[2, empty, 6]

Важно: map пропускает пустые (разрежённые) элементы, но они остаются в новом массиве на тех же позициях.

Mapping (отображение) в JavaScript - comments

En
Js mapping (javascript)