JS. Просто о сложном: filter, map, reduce
2026-02-26 15:13 Diff

Функции высшего порядка — элегантное решение, которое делает код проще, понятнее и эффективнее. Также при первом знакомстве они — верный источник головной боли, от которой не спасают ни гайды в интернете, ни попытки объяснить происходящее словами «свертка» и «отображение».

Этому весьма способствует сложившаяся традиция демонстрировать логику таких функций абстрактными примерами, в которых суммируются какие-то a и b:

Я предлагаю разобрать принцип действия filter, map и reduce на примере, приближенном к жизни и наконец-то разложить все по полочкам.

Содержание

Жизненный пример

Представим, что, отчаявшись разобраться с функциями высшего порядка самостоятельно, вы создаете топик в блоге с приблизительно таким заголовком:

'Пробую использовать функции высшего порядка, но ничего не выходит!!!!!'

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

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

Чтобы эффективно манипулировать таким массивом и использовать данные внутри, нам как раз очень пригодятся filter, map и reduce.

I. Filter

Функция filter самая простая и понятная из великолепной тройки. Она проходится по массиву и отбирает только те элементы, которые подходят под заданное условие. А те, которые не подходят, соответственно, игнорирует.

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

Применим filter:

Что здесь произошло? В функцию filter мы передали callback — по большому счету, просто функцию, аргументом которой является наш элемент коллекции — комментарий пользователя.

Для каждого элемента коллекции мы выбрали интересующую нас деталь, а именно, текст комментария comment.text, и проверили, содержит ли текст подстроку «console.log». Если это так, callback вернет true, и весь наш объект-комментарий будет добавлен в результат. В противном случае, callback вернет false, и результат не изменится.

Вот, кстати, и он:

Мы успешно отфильтровали комментарии по условию, дело сделано!

Рекомендую вам самостоятельно потестировать этот код на repl.it, чтобы убедиться, что все работает именно так. Последуйте совету Ивана Редьюсова — используйте отладку и посмотрите, как добавляются элементы ;)

II. Map

Функция map немного сложнее. Для каждого обработанного элемента коллекции она добавит в результат один элемент, измененный так, как мы укажем в callback функции.

В этом примере мы даже не изменили, а заменили элементы. Функция вернула 5 на каждый элемент изначального массива. Практического смысла в этом немного, но сам механизм вы должны понимать — мы могли бы вставить вместо числа массив или объект, и функция map также заполнила бы результирующий массив указанными сущностями в соотношении 1

(один элемент изначального массива — один элемент конечного массива, вне зависимости от его внутренней сложности).

Вернемся к нашему массиву комментариев. Допустим, мы хотим получить коллекцию имен всех пользователей, которые отписались в вашем топике:

Отображение сработало как надо, и мы получили интересующие нас детали. Попробуйте самостоятельно извлечь из комментариев другие элементы. Измените их прямо в callback функции — к примеру, извлеките рейтинг пользователей и переведите его в двоичную систему счисления!

Читайте также:

Что такое callback-функция в JavaScript?

III. Reduce

Функция reduce, наверное, самая сложная из нашей тройки, ведь, помимо элементов коллекции, в ней появляется аккумулятор, с которым нужно научиться правильно работать. Эта функция производит «свертку», то есть, берет элементы из коллекции и из их множества создает какую-то одну новую сущность. Например, из массива — объект или число.

Технически функция reduce может заменить и filter, и map, но это, скорее всего, введет в заблуждение ваших коллег-программистов, поэтому старайтесь применять каждую функцию по прямому назначению. Для фильтрации — фильтрацию, для свертки — свертку.

Теперь попробуем применить функцию reduce. Предположим, мы хотим, имея нашу коллекцию комментариев, создать объект, в котором ключами будут имена пользователей, а значениями — все комментарии данного пользователя.

Давайте пошагово разберем, что здесь происходит.

Из примечательного — у нас появляется аккумулятор. Это переменная, в которую мы будем складывать промежуточные результаты. Вспомните — функции высшего порядка обрабатывают элемент за элементом, а на выходе из reduce у нас должна получиться некая новая сущность. Ее мы будем наполнять последовательно, так что без аккумулятора не обойтись. В нашем случае переменная называется acc, но вы, конечно, можете придумать любое другое имя.

Во-вторых, у нас появляется инициализатор типа, к которому мы сводим коллекцию. В нашем случае это объект, поэтому ставим {}. На этом месте мог бы быть массив [], строка '' или число.

Теперь давайте разберемся с внутренней логикой callback функции.

В нашем примере возможны два сценария: в объекте уже есть ключ (имя пользователя), и тогда мы должны добавить в значения новый комментарий; или в объекте еще нет такого ключа, и тогда нам нужно его создать.

Что происходит здесь? Мы проверяем, есть ли в аккумуляторе искомый ключ. Для этого используем функцию _.has() из библиотеки Lodash. Если ключ есть, то добавляем текст комментария в значение ключа (оно представлено массивом).

И возвращаем аккумулятор — это важно!

Если же ключа нет, тогда добавляем его в наш результирующий объект, и передаем ему значение — массив с одним элементом, то есть, первым найденным нами комментарием этого пользователя. А с помощью spread-оператора мы копируем в объект всю накопленную аккумулятором информацию. Не забываем про возврат.

И вот он, долгожданный результат:

Как видите, из массива со множеством элементов мы создали один-единственный объект, зато наполнили его нужным нам содержимым. Попробуйте применить другую логику, например, вместо имени, используйте рейтинг пользователя.

IV. Бонус — функция forEach и особый синтаксис

Среди функций высшего порядка есть еще один любопытный экземпляр, функция forEach. Она используется для перебора элементов массива прямо как цикл for...of. Но, в отличие от цикла, более гибко встраивается в синтаксис функций высшего порядка.

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

Не стану подробно расписывать этот пример, думаю, теперь вы можете провернуть такую операцию самостоятельно!

Заключение

Спасибо, что прочитали эту статью! Надеюсь, функции высшего порядка стали вам немного понятнее и ближе. Практикуйтесь, и они станут вашими лучшими помощниками.

Желаю удачи в освоении JavaScript!