для чего нужен метод reduce
Array.prototype.reduce()
Метод reduce() применяет функцию reducer к каждому элементу массива (слева-направо), возвращая одно результирующее значение.
Источник этого интерактивного примера хранится в репозитории GitHub. Если вы хотите внести свой вклад в проект интерактивных примеров, скопируйте https://github.com/mdn/interactive-examples и отправьте нам запрос на перенос.
Синтаксис
Параметры
Описание
Метод reduce() выполняет функцию callback один раз для каждого элемента, присутствующего в массиве, за исключением пустот, принимая четыре аргумента: начальное значение (или значение от предыдущего вызова callback ), значение текущего элемента, текущий индекс и массив, по которому происходит итерация.
Предположим, что reduce() используется следующим образом:
Колбэк-функция будет вызвана четыре раза, аргументы и возвращаемое значение при каждом вызове будут следующими:
previousValue | currentValue | index | array | возвращаемое значение | |
---|---|---|---|---|---|
первый вызов | 0 | 1 | 1 | [0, 1, 2, 3, 4] | 1 |
второй вызов | 1 | 2 | 2 | [0, 1, 2, 3, 4] | 3 |
третий вызов | 3 | 3 | 3 | [0, 1, 2, 3, 4] | 6 |
четвёртый вызов | 6 | 4 | 4 | [0, 1, 2, 3, 4] | 10 |
accumulator | currentValue | index | array | возвращаемое значение | |
---|---|---|---|---|---|
первый вызов | 10 | 0 | 0 | [0, 1, 2, 3, 4] | 10 |
второй вызов | 10 | 1 | 1 | [0, 1, 2, 3, 4] | 11 |
третий вызов | 11 | 2 | 2 | [0, 1, 2, 3, 4] | 13 |
четвёртый вызов | 13 | 3 | 3 | [0, 1, 2, 3, 4] | 16 |
пятый вызов | 16 | 4 | 4 | [0, 1, 2, 3, 4] | 20 |
Примеры
Суммирование всех значений в массиве
Суммирование значений в массиве объектов
Пять интересных способов использования Array.reduce() (и один скучный путь)
Привет, Хабр! Представляю вашему вниманию перевод статьи «Five Interesting Ways to Use Array.reduce() (And One Boring Way)» автора Chris Ferdinandi.
Из всех современных методов работы с массивами самым сложным из всех, что мне пришлось использовать, был Array.reduce().
На первый взгляд он кажется простым, скучным методом, который мало что дает. Но, не смотря на свой скромный вид, Array.reduce() является мощным и гибким дополнением к вашему набору инструментов разработчика.
Сегодня рассмотрим некоторые интересные вещи, которые можно сделать с помощью Array.reduce().
Как работает Array.reduce()
Большинство современных методов массива возвращают новый массив. Метод Array.reduce() немного более гибкий. Он может вернуть все что угодно. Его цель — взять массив и сжать его содержимое в одно значение.
Это значение может быть числом, строкой или даже объектом или новым массивом. Это та часть, которая всегда сбивала меня с толку — я не понимал, насколько она гибкая!
Синтаксис
Array.reduce() принимает два аргумента: метод callback, выполняемый для запуска каждого элемента в массиве, и начальное значение initialValue.
Callback также принимает два аргумента: accumulator, который является текущим объединенным значением, и текущий элемент в цикле currentValue. Все, что вы возвращаете, используется в качестве accumulator для следующего элемента в цикле. В самом первом цикле вместо этого используется начальное значение.
Рассмотрим несколько примеров
1.Суммирование чисел
Допустим, у есть массив чисел, которые хотим сложить вместе. Используя Array.forEach(), можем сделать что-то вроде этого:
Это пример-клише для использования Array.reduce(). Слово «accumulator» сбивает с толку, поэтому в этом примере назовем его «sum», потому что это то, что оно есть по своей сути.
В обратном вызове мы добавляем текущее значение к сумме, которая имеет начальное значение 0 в первом цикле, затем 1 (начальное значение 0 плюс значение элемента 1), затем 3 (суммарное значение 1 плюс значение элемента 2) и так далее.
Пример.
2.Альтернатива комбинированию методов массива Array.map() и Array.filter() в одном шаге
Представим, что в Хогвартсе множество волшебников.
Хотим создать новый массив, который будет содержать только имена мастеров из Хаффлпаффа. Один из способов сделать это — использовать метод Array.filter(), чтобы получить обратно только тех волшебников, у которых свойство дома — Хаффлпафф. Затем используем метод Array.map() для создания нового массива, содержащего только свойство name для остальных мастеров.
С помощью метода Array.reduce() можно получить один и тот же массив за один проход, что улучшит нашу производительность. Передаем пустой массив ([]) в качестве начального значения. На каждом проходе проверяем, является ли wizard.house Хаффлпаффом. Если это так, отправляем его в newArr (наш accumulator в этом примере). Если нет, ничего не делаем.
В любом случае, возвращаем newArr, чтобы получить accumulator на следующем проходе.
3.Создание разметки из массива
Что если вместо создания массива имен, хотим создать неупорядоченный список мастеров в Хаффлпаффе? Вместо пустого массив в Array.reduce() в качестве нашего начального значения, передадим пустую строку (») и назовем ее html.
Если wizard.house равен Hufflepuff, мы объединяем нашу html-строку с wizard.name, обернутым в открывающий и закрывающий элементы списка (li). Затем вернем HTML, как accumulator в следующем цикле.
Добавим открывающий и закрывающий неупорядоченный элемент списка до и после Array.reduce(). Теперь все готово для добавления разметки в DOM.
4.Группировка похожих элементов в массив
В библиотеке lodash есть метод groupBy(), который принимает коллекцию элементов в виде массива и группирует их в объект на основе некоторых критериев.
Допустим, нам нужен массив чисел.
Если хотим сгруппировать все элементы в числа по их целочисленному значению, то сделать это следует с помощью lodash.
Если имеется массив слов, и нужно сгруппировать элементы в словах по их длине, мы бы это сделали.
Создание функции groupBy() с помощью Array.reduce()
Можно воссоздать ту же функциональность, используя метод Array.reduce().
Cоздадим вспомогательную функцию groupBy(), которая принимает массив и критерии для сортировки в качестве аргументов. Внутри groupBy() мы будем запускать Array.reduce() для нашего массива, передавая пустой объект (<>) в качестве отправной точки и возвращая результат.
Внутри Array.reduce() функцией callback проверим, является ли критерий функцией, применяемой к элементу, или же свойством элемента. Тогда мы получим его значение из текущего элемента.
Если в объекте пока нет свойства с этим значением, создадим его[свойство] и назначим пустой массив в качестве его значения. Наконец, добавим элемент в это свойство и вернем объект в качестве accumulator для следующего цикла.
Демонстрация завершенной вспомогательной функции.
Отдельное спасибо Тому Бремеру за помощь. Эту вспомогательную функцию и многое другое можно найти в Vanilla JS Toolkit.
5.Объединение данных из двух источников в массив
Вспомним наш список волшебников.
Что делать, если бы был другой набор данных — объект c домом и очками, которые заработал каждый маг.
Представим, что хотим объединить оба набора данных в один массив с количеством очков, добавленных к данным каждого волшебника в массиве wizards. Как это сделать?
Метод Array.reduce() идеально подходит для этого!
6.Объединение данных из двух источников в объект
Что, если вместо этого необходимо объединить два источника данных в объект, в котором имя каждого волшебника это ключ (key), а их дом и очки — свойства? Опять же, метод Array.reduce() идеально подходит для этого.
Стоит ли использовать Array.reduce()?
Метод Array.reduce() превратился из бессмысленного в мой любимый метод JavaScript. Итак, стоит ли его использовать? И когда же?
Метод Array.reduce() обладает фантастической поддержкой браузеров. Работает как во всех современных браузерах так и в IE9. Уже долгое время поддерживается мобильными браузерами. Если нужно еще больше, то можно добавить полифилл, чтобы вернуть поддержку в IE6.
Самой серьезной проблемой может быть то, что Array.reduce() сбивает с толку людей, которые никогда не сталкивались с ним[методом] раньше. Комбинация методов Array.filter() с Array.map() выполняется медленнее и включает дополнительные шаги, но ее легче читать. Из названий методов видно, что они должны делать.
Как уже было сказано, метод Array.reduce(), в целом, упрощает более сложные вещи. Хорошим примером является вспомогательная функция groupBy().
В конечном счете, это еще один инструмент для вашего инструментария. Инструмент, который, если его правильно использовать, может дать сверхспособности.
Об авторе
Крис Фердинанди помогает людям изучать ванильный JavaScript. Он считает, что есть более простой и надежный способ делать вещи для интернета.
Крис является автором серии Vanilla JS Pocket Guide, создателем учебной программы Vanilla JS Academy и ведущим Vanilla JS Podcast. Его бюллетень советов разработчикам читают тысячи разработчиков каждый будний день.
Он обучал разработчиков в таких организациях, как Chobani и Boston Globe, а его плагины JavaScript были использованы Apple и Гарвардской школой бизнеса. Крис Койер, основатель CSS-Tricks и CodePen, описал его работу как «бесконечно цитируемую».
Крис любит пиратов, щенков и фильмы Pixar, а также живет рядом с лошадиными фермами в сельской местности Массачусетса. Он ведет Go Make Things с щенком Бейли.
Использование map и reduce в функциональном JavaScript
Предлагаем вашему вниманию переводной материал об использовании map и reduce в функциональном JavaScript. Эта статья будет интересна в первую очередь начинающим разработчикам.
Замечание по производительности
Безусловно, читабельность и обслуживаемость кода не должны снижать производительность, если этого требует ситуация. Современные браузеры эффективнее выполняют более громоздкие традиционные конструкции, например, циклы.
Попробуйте следующую методику: сначала напишите код, исходя из критериев читабельности и обслуживаемости, а затем оптимизируйте его производительность, если в этом есть реальная потребность. Избегайте преждевременной оптимизации.
Также стоит отметить, что применение методов наподобие map() и reduce() позволит извлечь больше преимуществ из улучшений JS-движка, по мере того, как браузеры будут оптимизироваться для их использования. Если у вас нет проблем с производительностью, то лучше писать код с оптимистическим расчётом на будущее. А приёмы повышения производительности, делающие код менее опрятным, оставьте на потом, когда в них возникнет потребность.
Использование map
Маппинг — фундаментальная методика в функциональном программировании. Она применяется для оперирования всеми элементами массива с целью создания другого массива той же длины, но с преобразованным содержимым.
Чтобы было понятнее, давайте рассмотрим простой пример. Допустим, у вас есть массив слов, и вам нужно преобразовать его в массив, содержащий длины всех слов исходного массива. Да, это не самый актуальный случай, но понимание того, как работает этот инструмент, поможет вам применять его в дальнейшем для улучшения своего кода.
Здесь определено несколько переменных:
Вот как можно решить нашу задачу с помощью map() :
Обратите внимание, что при таком подходе:
Посмотрите, насколько чище стал выглядеть код. Просто начните применять маппинг, и сразу выйдете на новый уровень функционального программирования.
Что такое функтор?
Любопытно, что при добавлении маппинга в объект массива, ECMAScript 5 превращает основной тип массива в полный функтор. Это делает функциональное программирование ещё более доступным.
Согласно классическим определениям функционального программирования, функтор удовлетворяет трём критериям:
Использование reduce
Опять же, чисто технически здесь всё в порядке. Обработали массив, получили результат. Но с помощью метода reduce() можно это сделать гораздо проще:
Заключение
Помимо map() и reduce() в ECMAScript 5 появились и другие новые методы. Вероятно, улучшение качества кода и удовольствие от разработки, которое вы прочувствуете, намного перевесят временное ухудшение производительности. Используйте функциональные подходы и измеряйте влияние на производительность в реальных проектах, вместо того, чтобы думать, а нужны ли map() и reduce() в вашем приложении.
Я слышал, что это — базовые вещи, понимание которых является чем-то вроде границы между «посвящёнными» и «непосвящёнными». Хотелось бы мне тогда, чтобы мне сказали о них правду. Она заключается в том, что эти три метода символизируют то, что причины, по которым перебирают некие итерируемые объекты, часто вписываются в одну из трёх функциональных категорий.
Просматривая код, который я писал раньше, я понял, что в 95% случаев, когда я перебирал элементы строк или массивов, я выполнял одно из следующих действий:
Для того чтобы попрактиковаться, я взял свой старый код и отрефакторил его с использованием этих методов. Это оказалось весьма полезным занятием.
А теперь, без лишних слов, давайте поговорим об этих методах, и, в частности, посмотрим на то, как использовать их вместо широко распространённых схем применения циклов.
Имя параметра price — это то имя, которое будет использоваться при работе с элементами массива. Так как наша стрелочная функция имеет всего один параметр — мы можем обойтись без круглых скобок при её объявлении.
Если такая запись кажется вам непонятной — вот немного расширенный вариант этого примера:
Рассмотрим пример, в котором нужно отобрать из массива целых чисел только нечётные элементы. Здесь мы воспользуемся оператором взятия остатка от деления и будем выяснять — имеется ли остаток от деления каждого элемента массива на 2. Если остаток равен 1 — это говорит нам о том, что соответствующее число является нечётным. Сначала взглянем на способ решения этой задачи с помощью обычного цикла:
При конструировании кода, в котором вызывается этот метод, сначала задают некое начальное значение. По мере того, как метод перебирает значения массива, это начальное значение модифицируется и, в изменённом виде, передаётся в следующую итерацию.
Вот классическая задача, для решения которой нужно вычислить сумму элементов массива. В нашем случае она заключается в поиске суммы пожертвований на некий благотворительный проект:
Итоги
Массив: перебирающие методы
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/array-methods.
Современный стандарт JavaScript предоставляет много методов для «умного» перебора массивов, которые есть в современных браузерах…
…Ну а для их поддержки в IE8- просто подключите библиотеку ES5-shim.
forEach
Метод «arr.forEach(callback[, thisArg])» используется для перебора массива.
Этой функции он передаёт три параметра callback(item, i, arr) :
filter
Метод «arr.filter(callback[, thisArg])» используется для фильтрации массива через функцию.
Метод «arr.map(callback[, thisArg])» используется для трансформации массива.
every/some
Эти методы используются для проверки массива.
reduce/reduceRight
Метод «arr.reduce(callback[, initialValue])» используется для последовательной обработки каждого элемента массива с сохранением промежуточного результата.
Это один из самых сложных методов для работы с массивами. Но его стоит освоить, потому что временами с его помощью можно в несколько строк решить задачу, которая иначе потребовала бы в разы больше места и времени.
Метод reduce используется для вычисления на основе массива какого-либо единого значения, иначе говорят «для свёртки массива». Чуть далее мы разберём пример для вычисления суммы.
Он применяет функцию callback по очереди к каждому элементу массива слева направо, сохраняя при этом промежуточный результат.
Аргументы функции callback(previousValue, currentItem, index, arr) :
Проще всего понять работу метода reduce на примере.
Например, в качестве «свёртки» мы хотим получить сумму всех элементов массива.
Вот решение в одну строку:
Разберём, что в нём происходит.
При первом запуске sum – исходное значение, с которого начинаются вычисления, равно нулю (второй аргумент reduce ).
Сначала анонимная функция вызывается с этим начальным значением и первым элементом массива, результат запоминается и передаётся в следующий вызов, уже со вторым аргументом массива, затем новое значение участвует в вычислениях с третьим аргументом и так далее.
Поток вычислений получается такой
В виде таблицы где каждая строка – вызов функции на очередном элементе массива:
sum | current | результат | |
---|---|---|---|
первый вызов | 0 | 1 | 1 |
второй вызов | 1 | 2 | 3 |
третий вызов | 3 | 3 | 6 |
четвёртый вызов | 6 | 4 | 10 |
пятый вызов | 10 | 5 | 15 |
Как видно, результат предыдущего вызова передаётся в первый аргумент следующего.
Посмотрим, что будет, если не указать initialValue в вызове arr.reduce :
Результат – точно такой же! Это потому, что при отсутствии initialValue в качестве первого значения берётся первый элемент массива, а перебор стартует со второго.
Таблица вычислений будет такая же, за вычетом первой строки.
Метод arr.reduceRight работает аналогично, но идёт по массиву справа-налево.
Итого
Мы рассмотрели методы: