> Для чего нужны и как работают методы массивов map, filter и reduce в JavaScript (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: The Tribe, Софт Медиа Групп, Яндекс

Стек: JavaScript

> Пример ответа

Короткий ответ

map, filter и reduce - это методы массива, которые реализуют функциональный подход к обработке данных. map преобразует каждый элемент массива и возвращает новый массив той же длины. filter возвращает новый массив, содержащий только элементы, удовлетворяющие условию. reduce сводит массив к одному значению, аккумулируя результат через callback. Все три метода иммутабельны - не изменяют исходный массив, что важно для предсказуемости кода.

Подробное объяснение

Эти методы появились в ES5 (кроме reduce, который тоже в ES5) и стали основой для декларативного стиля программирования в JavaScript. Их ключевое отличие от циклов - отсутствие побочных эффектов и явного управления индексами.

map - принимает callback (element, index, array) => newElement. Вызывается для каждого элемента, результат каждого вызова попадает в новый массив. Важно: если callback ничего не возвращает, в результирующем массиве будет undefined. Метод не изменяет исходный массив, но если элементы - объекты, они передаются по ссылке, и мутация внутри callback повлияет на оригинал.

filter - callback должен вернуть truthy/falsy значение. Элементы, для которых callback вернул truthy, попадают в новый массив. Порядок элементов сохраняется. Метод не изменяет длину исходного массива, но может вернуть пустой массив.

reduce - принимает callback (accumulator, currentValue, index, array) => newAccumulator и необязательное начальное значение. Если начальное значение не передано, в качестве первого accumulator берётся первый элемент массива, а итерация начинается со второго. Это важный edge case: для пустого массива без начального значения будет выброшен TypeError. Для массива из одного элемента без начального значения метод вернёт этот элемент, не вызывая callback.

Все три метода пропускают "дырки" в разреженных массивах (sparse arrays), но reduce ведёт себя иначе: если в массиве есть пропуски, callback не вызывается для них, но при отсутствии начального значения и наличии "дырок" в начале, поведение может быть неочевидным.

На практике

В реальном коде эти методы часто комбинируют. Типичный паттерн - цепочка filter().map().reduce(). Важно помнить, что каждая операция создаёт новый массив, поэтому для очень больших массивов (сотни тысяч элементов) это может дать оверхед по памяти. В таких случаях можно использовать один reduce, чтобы пройти массив один раз.

reduce часто используют не только для суммирования, но и для группировки, создания объектов из массива, реализации pipe или compose. Например, с его помощью можно превратить массив пар в объект или сгруппировать элементы по ключу.

При работе с асинхронным кодом эти методы не подходят напрямую - они не ждут промисы. Для последовательной обработки с await используют for...of или reduce с Promise.resolve() в аккумуляторе.

Пример кода

JAVASCRIPT
const numbers = [1, 2, 3, 4, 5, 6];
// map: удвоить чётные числа
const doubledEvens = numbers
.filter(n => n % 2 === 0)
.map(n => n * 2);
// [4, 8, 12]
// reduce: сумма всех чисел
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 21
// reduce: группировка по чётности
const grouped = numbers.reduce((acc, n) => {
const key = n % 2 === 0 ? 'even' : 'odd';
acc[key].push(n);
return acc;
}, { even: [], odd: [] });
// { even: [2, 4, 6], odd: [1, 3, 5] }
// reduce: преобразование массива пар в объект
const pairs = [['name', 'Alice'], ['age', 30]];
const obj = pairs.reduce((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
// { name: 'Alice', age: 30 }
// Ошибка: пустой массив без начального значения
// [].reduce((acc, n) => acc + n); // TypeError

Как отвечать на собеседовании

Начните с краткого определения каждого метода, затем переходите к деталям. Обязательно упомяните иммутабельность и отличие от forEach. Покажите, что понимаете нюансы: отсутствие начального значения в reduce, поведение с пустым массивом, передачу объектов по ссылке. Приведите пример комбинации методов и объясните, когда лучше использовать reduce вместо цепочки filter + map. Если спросят про производительность, честно скажите, что для большинства задач разница незначительна, но для больших данных стоит рассмотреть один проход через reduce. Также упомяните, что методы не работают с асинхронными операциями без дополнительных обёрток.

Что проверяет интервьюер

Интервьюер оценивает не только знание синтаксиса, но и понимание функциональной парадигмы. Важно показать, что вы осознаёте: эти методы - не просто замена циклов, а инструмент для написания декларативного, читаемого кода. Проверяется умение работать с аккумулятором в reduce, понимание edge case'ов и способность объяснить, почему код без побочных эффектов легче тестировать и поддерживать. Также оценивается, можете ли вы выбрать правильный метод под задачу: map для трансформации, filter для выборки, reduce для агрегации.

Типичные ошибки

  • Забывают вернуть значение из callback в map - получают массив из undefined.
  • Используют filter для трансформации элементов - это ошибка, filter только выбирает.
  • Не передают начальное значение в reduce и получают ошибку на пустом массиве.
  • Мутируют исходный массив внутри callback, думая, что методы иммутабельны.
  • Пытаются использовать async callback напрямую в map или filter - получают массив промисов.
  • Путают порядок аргументов в reduce: (accumulator, currentValue), а не наоборот.
  • Используют reduce для простых операций, где map и filter читаются лучше.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью