Источник: Nuances of Programming
Функции редьюсера просты, но их сложно освоить. Минималистичные решения, которые они реализуют, часто слишком абстрактны для начинающих разработчиков. Трудно представить почти безграничные возможности их использования для управления данными и их упрощения. Поэтому для большинства программистов редьюсеры — это тайна, покрытая мраком.
Давайте приподнимем эту завесу таинственности, пополним багаж знаний о них и напишем что-нибудь на JavaScript! Начнём с основ: узнаем, что такое редьюсеры и как выглядит функция reduce. Затем создадим более сложные функции и пошагово разберём их выполнение. И наконец, применим эти функции к результатам, полученным с API базы данных о фильмах theMovieDb.org. Итак, приступим!
Основы
Библиотеки типа Redux задействуют редьюсеры для управления состоянием больших веб-приложений. Однако функции редьюсера можно использовать и для изменения состояния отдельной структуры данных.
Рассмотрим пример, иллюстрирующий свёртку массива до целого числа:
Здесь редьюсер add() принимает два аргумента: аккумулятор и текущее значение. В методе Array.Reduce() выполняется редьюсер, а его возвращаемое значение устанавливается равным аккумулятору. Это продолжится до тех пор, пока итерация массива не завершится. При вызове аккумулятор имеет исходное значение. Это необязательная переменная. Если исходное значение не задано, аккумулятору устанавливается значение первого элемента массива.
Если пока немного непонятно, не переживайте! Мы скоро всё подробно разберём. Начнём с создания функции, которая воспроизводит поведение Array.Reduce():
Функция reduce принимает три аргумента: массив для свёртки к единому значению, функцию редьюсера для выполнения и исходное значение для аккумулятора. Используем .shift() для установки первого элемента массива в качестве исходного значения, если initValue не определено. Затем перебираем массив, устанавливая аккумулятор равным результату функции редьюсера. Это продолжится до тех пор, пока в аккумуляторе не останется один результат. И в завершение мы возвращаем аккумулятор. Посмотрим, как это работает. Вот подробный, поитеративный разбор Reduce:
В этом примере показывается, как значение аккумулятора на каждой итерации увеличивается, а текущее значение добавляется к аккумулятору через функцию редьюсера (в нашем случае это функция add). Усвоив всю эту информацию, вы будете на правильном пути к пониманию метода reduce!
Следующий этап
Допустим, у нас есть большой набор данных, который надо свести к пересекающимся результатам. Как это сделать? Одно из решений — выполнить перебор данных и создать единый массив, содержащий все перекрывающиеся результаты. Начнём с написания функции, которая сворачивает всю пересекающуюся информацию:
Не забывайте, что reduce() заменит аккумулятор на возвращаемое значение функции reducer. А reducer в intersectionWithReduce() отсеивает все элементы массива currentArray, которые не включены в аккумулятор. Затем в аккумулятор устанавливается возвращаемое значение прошедшего фильтрацию массива currentArray. Это продолжится до тех пор, пока в аккумуляторе не останется один массив всех пересекающихся значений. Посмотрим, как это делается:
И вот теперь начинаем задействовать потенциал редьюсеров! Напишем функцию, которая создаёт единый массив всех результатов без дублирования:
joinWithReduce() конкатенирует или добавляет к аккумулятору значения currentArray, которые ещё не включены в аккумулятор. Надеюсь, вы уже можете предвидеть, как будет выполняться эта функция. Разберём её в последний раз:
Reduce в действии
А теперь испытаем reduce в деле: используем созданные функции для сворачивания данных, полученных на запросы к API. Задача — найти фильмы, которые входят в 20 лучших по кассовым сборам и количеству голосов пользователей. Затем объединим всё это с результатами API-запросов. Первым делом надо запросить эти результаты с URL-адреса конечной точки:
Мы написали асинхронную функцию movies(), чтобы вернуть объект promise с данными. Промисы позволяют писать неблокирующий асинхронный код. Давайте создадим промис для каждого ответа с API и задействуем метод Promise.All() для создания массива результатов, возвращаемых из объектов promise:
Теперь будем сворачивать названия фильмов, поэтому соберём всю эту информацию в массив:
Object.Value() преобразует response object (объект-ответ) в массив значений. После чего getTitles() отделяет от ответа данные о фильмах и использует их для создания массива с названиями фильмов. Теперь всё готово к реализации функций! Вызовем в данных функцию intersectionWithReduce() и посмотрим, что мы получим:
intersectingTitles содержит массив из четырёх очень популярных фильмов. Теперь для объединения данных используем joinWithReduce():
В joinTitles у нас массив из 36 фильмов. Этот список не содержит дублей тех четырёх фильмов из двух первых массивов.
Заключение
Надеюсь, эта статья помогла вам понять, как с помощью reduce можно прокачать навыки работы с JavaScript. Код для финальной задачи доступен здесь. В нём есть все созданные нами функции. Спасибо за внимание и не забывайте продолжать работать кодом!
Читайте также:
Перевод статьи Anthony Jimenez: Learn These Three JavaScript Functions and Become a Reduce Master!