Когда я научился использовать .map(), .filter() и .reduce(), все, что я читал, смотрел и слышал, звучало так сложно. Эти концепции преподавались как самостоятельные реализации, которые я не мог обернуть вокруг себя.

Просматривая ранее написанный код, я понял, что в 95% случаев при циклическом просмотре строк или массивов я выполняю одно из следующих действий: сопоставление последовательности операторов с каждым значением (map), фильтрация значений, соответствующих определенным критериям (filter), или обработка каждого элемента массива с сохранением промежуточного результата. (reduce).

Это был мой прорывной момент: map, filter и reduce каждый из них, просто выполнив одну из этих задач!

Для практики я взял свой старый код и переписал его, используя эти методы. Это было чрезвычайно полезно.

Давайте погрузимся…

Map

Метод .map() используется, когда вы хотите
1. выполнить набор операторов с каждым значением в итерируемом
2. вернуть (предположительно) измененное значение.

Давайте рассмотрим простой пример расчета налога с продаж по ряду цен.

const prices = [19.99, 4.95, 25, 3.50];
let new_prices = [];
for(let i=0; i < prices.length; i++) {
   new_prices.push(prices[i] * 1.06);
}

Мы можем достичь тех же результатов, используя .map():

const prices = [19.99, 4.95, 25, 3.50];
let new_prices = prices.map(price => price * 1.06);

Синтаксис выше сжат, поэтому давайте немного разберемся с ним. Метод .map() принимает обратный вызов, который можно рассматривать как функцию. Вот что между скобками.

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

Оператор после стрелки => является телом нашего обратного вызова. Поскольку в теле есть только один оператор, мы можем опустить фигурные скобки, а также ключевое слово return.

На всякий случай, если это все еще сбивает с толку, давайте напишем это полностью для справки:

const prices = [19.99, 4.95, 25, 3.50];
let new_prices = prices.map((price) => {
   return price * 1.06
});

Filter

Перейдем к методу .filter(), который используется, когда вы хотите извлечь подмножество значений из итерируемого. При использовании .filter() помните, что мы фильтруем значения, а не отфильтровываем. Это означает, что каждый элемент итерируемого, который оценивает true, будет включен в фильтр.

Давайте использовать пример хранения только нечетных целых чисел. Мы используем оператор модуля для вычисления остатка от деления на 2. Когда этот остаток равен 1, мы знаем, что число было нечетным.

const numbers = [1,2,3,4,5,6,7,8];
let odds = [];
for(let i=0; i < numbers.length; i++) {
   if(numbers[i] % 2 == 1) {
      odds.push(numbers[i]);
   }
}

Подобно методу .map(), .filter() принимает один обратный вызов, куда будет передано каждое значение в итерируемом.

const numbers = [1,2,3,4,5,6,7,8];
let odds = numbers.filter(num => num % 2);

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

Reduce

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

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

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

const donations = [5, 20, 100, 80, 75];
let total = 0;
for(let i=0; i < donations.length; i++) {
   total += donations[i];
}

В отличие от .map() и .filter(), для обратного вызова метода .reduce() требуются два параметра: аккумулятор и текущее значение. Аккумулятор будет первым параметром и является значением «pass it down».

const donations = [5, 20, 100, 80, 75];
let total = donations.reduce((total,donation) => {
   return total + donation;
});

Мы также можем передать второй аргумент самому методу .reduce(). Это будет служить начальным значением для аккумулятора. Допустим, мы добавляем вчерашние пожертвования на общую сумму 450 долларов.

const donations = [5, 20, 100, 80, 75];
let total = donations.reduce((total,donation) => {
   return total + donation;
}, 450);

Эти методы не страшны! Думайте о них как о том, как сделать ваш код более читабельным. Вы пишете более сжатый код, но, что более важно, вы на самом деле описываете намерение вашего цикла.
Вам будет намного легче читать код, если вы вернетесь к нему через три месяца. Вместо того, чтобы читать операторы внутри цикла for, просто чтобы понять его намерения высокого уровня, вы можете увидеть map() / filter() / reduce() и начать понимать, чего пытается достичь блок.

About the Author

Ergashev Lazizbek

Добрый день, дорогие мои читатели, позвольте мне рассказать вам немного о себе. Я Лазизбек Эргашев, я веб-разработчик из Узбекистана. В основном я использую laravel/php для бэкэнда и vuejs/javascript для фронтэнда. Основная цель моего блога - поделиться с вами своим опытом и знаниями.

View All Articles