Bezdepbonusu7Программирование: статьи и ответы

JavaScript на практике

Методы массивов JavaScript: map, filter, reduce, forEach

Редакция Bezdepbonusu77 мин чтения

Методы массивов JavaScript заменяют большинство циклов: map преобразует данные, filter отбирает, reduce считает итог, forEach просто перебирает. Ниже разбор каждого метода на одном наборе товаров, таблица мутирующих методов и ошибки, которые встречаются чаще всего.

Код на JavaScript с методами массивов map, filter и reduce
Фото: winkelnkemper, CC BY-SA 2.0, Wikimedia Commons
Содержание
  1. Обзор методов
  2. forEach
  3. map
  4. filter
  5. reduce
  6. Цепочки методов
  7. Другие методы
  8. Что выбрать
  9. Частые вопросы

Методы массивов JavaScript: шпаргалка

У массивов в JavaScript больше 30 встроенных методов, но в ежедневной работе хватает десятка. Главное, что нужно понять сразу: одни методы возвращают новый массив и не трогают исходный, другие меняют массив на месте. Перепутаете - получите баг, который трудно найти, особенно в React, где состояние нельзя мутировать.

МетодЧто делаетВозвращаетМеняет исходный
forEachВыполняет функцию для каждого элементаundefinedНет
mapПреобразует каждый элементНовый массив той же длиныНет
filterОтбирает элементы по условиюНовый массивНет
reduceСводит массив к одному значениюЛюбое значениеНет
find / findIndexИщет первый подходящий элементЭлемент или undefined / индекс или -1Нет
some / everyПроверяет условие хотя бы для одного / для всехtrue или falseНет
includesЕсть ли значение в массивеtrue или falseНет
sliceКопирует часть массиваНовый массивНет
sort, reverseСортирует, разворачиваетТот же массивДа
push, pop, shift, unshiftДобавляет и удаляет с краевДлину или удаленный элементДа
spliceУдаляет и вставляет в серединеМассив удаленныхДа

Все примеры ниже можно запустить в консоли браузера (F12 - Console) или в Node.js. Для них возьмем один массив товаров:

const products = [
  { name: "Ноутбук", price: 85000, category: "tech", inStock: true },
  { name: "Мышь", price: 1200, category: "tech", inStock: false },
  { name: "Кружка", price: 600, category: "home", inStock: true },
  { name: "Лампа", price: 3400, category: "home", inStock: true },
];

forEach: перебор массива

forEach вызывает функцию для каждого элемента и ничего не возвращает. Функция получает три аргумента: элемент, его индекс и сам массив. Обычно нужны первые два.

products.forEach((item, i) => {
  console.log(`${i + 1}. ${item.name} - ${item.price} руб.`);
});

Используйте forEach, когда нужен побочный эффект: вывести в консоль, отправить запрос, изменить DOM. Если вы хотите получить новый массив, берите map или filter.

Два ограничения. Из forEach нельзя выйти досрочно через break, а return внутри только пропускает текущую итерацию. Если нужен ранний выход, используйте обычный цикл for...of или методы some и find, которые останавливаются сами. И forEach не ждет асинхронные функции: arr.forEach(async x => await save(x)) запустит все запросы разом и не дождется ни одного. Для последовательных запросов пишите for...of с await, для параллельных - await Promise.all(arr.map(save)).

map: преобразование каждого элемента

map создает новый массив, в котором каждый элемент - результат вашей функции. Длина результата всегда равна длине исходного массива.

const names = products.map(p => p.name);
// ['Ноутбук', 'Мышь', 'Кружка', 'Лампа']

const withVat = products.map(p => ({ ...p, price: Math.round(p.price * 1.2) }));
// исходный products не изменился

Обратите внимание на скобки вокруг объекта во втором примере. Без них p => { ...p } JavaScript воспримет фигурные скобки как тело функции, и вы получите синтаксическую ошибку или массив из undefined. Та же беда случается, если в стрелочной функции с фигурными скобками забыть return:

[1, 2, 3].map(x => { x * 2 });          // [undefined, undefined, undefined]
[1, 2, 3].map(x => { return x * 2; });  // [2, 4, 6]
[1, 2, 3].map(x => x * 2);              // [2, 4, 6]

В React map - основной способ вывести список: items.map(item => <li key={item.id}>{item.name}</li>). Атрибут key с уникальным значением обязателен, индекс массива в качестве ключа подходит только для статичных списков.

Ловушка с parseInt

Классический вопрос с собеседований: что вернет ["1", "2", "3"].map(parseInt)? Ответ [1, NaN, NaN]. map передает в функцию второй аргумент - индекс, а parseInt принимает его как систему счисления. Пишите явно: .map(s => parseInt(s, 10)) или .map(Number).

filter: отбор элементов по условию

filter возвращает новый массив только из тех элементов, для которых функция вернула истинное значение.

const available = products.filter(p => p.inStock);
const cheapHome = products.filter(p => p.category === "home" && p.price < 1000);
// [{ name: 'Кружка', ... }]

const clean = [0, "a", null, "b", undefined, ""].filter(Boolean);
// ['a', 'b']

Последний прием убирает из массива все ложные значения: 0, пустую строку, null, undefined, NaN. Осторожно, если ноль - нормальное значение, он тоже пропадет.

Если нужен только первый подходящий элемент, а не все, используйте find: он остановится на первом совпадении и вернет сам объект, а не массив из одного элемента. Для удаления элемента из массива в React-состоянии filter - стандартный способ: setItems(items.filter(i => i.id !== id)).

reduce: свести массив к одному значению

reduce - самый гибкий и самый пугающий метод. Он проходит по массиву и копит результат в аккумуляторе. Первый аргумент - функция (acc, item) => новыйAcc, второй - начальное значение аккумулятора.

const total = products.reduce((sum, p) => sum + p.price, 0);
// 90200

const byCategory = products.reduce((acc, p) => {
  acc[p.category] = (acc[p.category] || 0) + 1;
  return acc;
}, {});
// { tech: 2, home: 2 }

const maxPrice = products.reduce((max, p) => Math.max(max, p.price), -Infinity);

Как это работает пошагово на сумме:

  1. Аккумулятор sum равен начальному значению 0.
  2. Для ноутбука функция возвращает 0 + 85000, это новое значение sum.
  3. Для мыши: 85000 + 1200 = 86200.
  4. Для кружки и лампы: 86800, затем 90200.
  5. Последнее возвращенное значение становится результатом reduce.

Всегда передавайте начальное значение вторым аргументом. Без него reduce возьмет первый элемент массива как аккумулятор: на массиве объектов вы получите строку вида "[object Object]1200..." вместо суммы, а на пустом массиве - TypeError. И не забывайте return acc в функциях с фигурными скобками, это ошибка номер один в reduce.

Для группировки объектов в современных браузерах и Node.js 21+ есть готовый Object.groupBy(products, p => p.category), он часто читается проще, чем reduce.

Цепочки map, filter и reduce

Методы, которые возвращают массив, можно вызывать друг за другом. Получается читаемый конвейер обработки данных.

const sumInStock = products
  .filter(p => p.inStock)        // только в наличии
  .map(p => p.price)             // берем цены
  .reduce((s, x) => s + x, 0);   // складываем
// 89000

Каждый шаг создает промежуточный массив. Для списков из сотен и тысяч элементов это незаметно. Если массив на миллионы элементов и код в горячем цикле, один reduce или обычный цикл будет быстрее, но начинать стоит с читаемого варианта и оптимизировать после замера.

Похожие задачи часто приходят с сервера в формате JSON: вы получаете массив объектов из API и фильтруете его перед выводом. Как устроены такие ответы, описано в статьях что такое API и JSON: что это.

find, some, every, sort и новые методы

products.find(p => p.price > 3000);      // { name: 'Ноутбук', ... }
products.findIndex(p => p.name === "Лампа"); // 3
products.some(p => !p.inStock);           // true - есть хоть один не в наличии
products.every(p => p.price > 500);       // true
[1, 2, 3].includes(2);                     // true
[[1, 2], [3]].flat();                      // [1, 2, 3]
products.at(-1).name;                      // 'Лампа' - последний элемент

Сортировка без сюрпризов

sort без аргументов сравнивает элементы как строки: [10, 9, 1].sort() даст [1, 10, 9]. Для чисел передайте функцию сравнения (a, b) => a - b, для русских строк используйте localeCompare. И помните, что sort меняет исходный массив.

const byPrice = [...products].sort((a, b) => a.price - b.price);
const byName = products.toSorted((a, b) => a.name.localeCompare(b.name));

Методы toSorted, toReversed, toSpliced и with появились в стандарте ES2023. Они делают то же, что sort, reverse и splice, но возвращают копию и не мутируют исходный массив. Поддерживаются в актуальных браузерах и Node.js 20+. Для старых окружений используйте копию через spread, как в первой строке.

Какой метод выбрать: краткая памятка

ЗадачаМетод
Получить массив того же размера с измененными элементамиmap
Оставить часть элементовfilter
Посчитать сумму, среднее, собрать объектreduce
Найти один элементfind
Проверить, есть ли подходящийsome
Проверить, что подходят всеevery
Сделать что-то для каждого без результатаforEach или for...of
Отсортировать без изменения исходногоtoSorted или [...arr].sort()

Если только начинаете путь во фронтенде и выбираете направление, посмотрите сравнение frontend или backend. Поиск по строкам внутри массивов удобно делать регулярками, их разбор в статье регулярные выражения: шпаргалка. Остальные материалы - в теге JavaScript.

Частые вопросы

Чем map отличается от forEach?

map возвращает новый массив из результатов функции, forEach ничего не возвращает и нужен для побочных действий вроде вывода в консоль. Если результат вы не используете, берите forEach или for...of, если используете - map.

Какие методы массива изменяют исходный массив?

push, pop, shift, unshift, splice, sort, reverse, fill и copyWithin. Методы map, filter, reduce, slice, concat, а также toSorted, toReversed и toSpliced возвращают новый результат и исходный массив не трогают.

Зачем передавать начальное значение в reduce?

Без него reduce использует первый элемент как аккумулятор. На пустом массиве это вызовет TypeError, а на массиве объектов даст неверный результат. С начальным значением, например 0 или {}, поведение предсказуемое.

Как выйти из forEach досрочно?

Никак: break в forEach не работает, а return лишь пропускает текущую итерацию. Используйте for...of с break, либо методы some, every или find, которые останавливаются сами.

Почему sort неправильно сортирует числа?

Без функции сравнения sort приводит элементы к строкам и сравнивает их посимвольно, поэтому 10 оказывается перед 9. Передайте функцию (a, b) => a - b.

Источники

  • Array - JavaScript, MDN Web Docs, 2026
  • Методы массивов, Современный учебник JavaScript (learn.javascript.ru), 2026
  • ECMAScript 2023 Language Specification, Ecma International, 2023

JavaScriptReact

Похожие материалы

Версии программ и команды проверены на дату публикации. Если что-то изменилось, сверяйтесь с официальной документацией. Нашли неточность? Напишите на admin@bezdepbonusu7.win.