Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Когда я разбирался в том, как пользоваться методами JS-массивов
Я слышал, что это — базовые вещи, понимание которых является чем-то вроде границы между «посвящёнными» и «непосвящёнными». Хотелось бы мне тогда, чтобы мне сказали о них правду. Она заключается в том, что эти три метода символизируют то, что причины, по которым перебирают некие итерируемые объекты, часто вписываются в одну из трёх функциональных категорий.
Просматривая код, который я писал раньше, я понял, что в 95% случаев, когда я перебирал элементы строк или массивов, я выполнял одно из следующих действий:
Это был момент истины. Именно тогда я понял суть этих методов и увидел их связь с тем, что мне уже давно известно.
Для того чтобы попрактиковаться, я взял свой старый код и отрефакторил его с использованием этих методов. Это оказалось весьма полезным занятием.
А теперь, без лишних слов, давайте поговорим об этих методах, и, в частности, посмотрим на то, как использовать их вместо широко распространённых схем применения циклов.
Метод
Рассмотрим простой пример, в котором для каждого элемента массива, содержащего цены, нужно найти новые суммы, включающие в себя изначальные цены и налог с продаж:
Вот как сделать то же самое с помощью
Тут используются довольно-таки лаконичные синтаксические конструкции. Поэтому давайте разберём этот пример. Метод
Имя параметра
Выражение после стрелки (
Если такая запись кажется вам непонятной — вот немного расширенный вариант этого примера:
Метод
Рассмотрим пример, в котором нужно отобрать из массива целых чисел только нечётные элементы. Здесь мы воспользуемся оператором взятия остатка от деления и будем выяснять — имеется ли остаток от деления каждого элемента массива на 2. Если остаток равен 1 — это говорит нам о том, что соответствующее число является нечётным. Сначала взглянем на способ решения этой задачи с помощью обычного цикла:
Метод
Тут работа организована так же, как и в примере с
И вот мы, наконец, добрались до метода
При конструировании кода, в котором вызывается этот метод, сначала задают некое начальное значение. По мере того, как метод перебирает значения массива, это начальное значение модифицируется и, в изменённом виде, передаётся в следующую итерацию.
Вот классическая задача, для решения которой нужно вычислить сумму элементов массива. В нашем случае она заключается в поиске суммы пожертвований на некий благотворительный проект:
В отличие от методов
Методу
Надеюсь, теперь вы разобрались с методами JS-массивов
Благодаря этим методам код, который написан довольно давно, будет легче читать. Вместо того, чтобы вникать в конструкции, помещённые внутрь циклов
Уважаемые читатели! Пользуетесь ли вы методами JS-массивов .map(), .filter() и .reduce()?
.map()
, .filter()
и .reduce()
, всё, что я читал, смотрел и слушал, казалось мне очень сложным. Эти концепции рассматривались как некие самостоятельные механизмы, ни к чему другому отношения не имеющие. Мне тяжело было ухватить их суть и их понять.Я слышал, что это — базовые вещи, понимание которых является чем-то вроде границы между «посвящёнными» и «непосвящёнными». Хотелось бы мне тогда, чтобы мне сказали о них правду. Она заключается в том, что эти три метода символизируют то, что причины, по которым перебирают некие итерируемые объекты, часто вписываются в одну из трёх функциональных категорий.
Просматривая код, который я писал раньше, я понял, что в 95% случаев, когда я перебирал элементы строк или массивов, я выполнял одно из следующих действий:
- Применение к каждому значению некоей последовательности инструкций (аналог метода
.map()
). - Фильтрация значений, соответствующих заданному критерию (то же, что делает
.filter()
). - Сведение набора данных к единственному агрегированному значению (аналог
.reduce()
).
Это был момент истины. Именно тогда я понял суть этих методов и увидел их связь с тем, что мне уже давно известно.
Для того чтобы попрактиковаться, я взял свой старый код и отрефакторил его с использованием этих методов. Это оказалось весьма полезным занятием.
А теперь, без лишних слов, давайте поговорим об этих методах, и, в частности, посмотрим на то, как использовать их вместо широко распространённых схем применения циклов.
Метод .map()
Метод
.map()
используется в том случае, если нужно сделать следующее:- Надо выполнить над каждым элементом итерируемого объекта некую последовательность действий.
- Надо вернуть значение, которое, предположительно, было изменено.
Рассмотрим простой пример, в котором для каждого элемента массива, содержащего цены, нужно найти новые суммы, включающие в себя изначальные цены и налог с продаж:
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]);
}
}
Метод
.filter()
, как и .map()
, принимает один коллбэк, которому будут поочерёдно передаваться элементы итерируемого объекта:const numbers = [1,2,3,4,5,6,7,8];
let odds = numbers.filter(num => num % 2);
Тут работа организована так же, как и в примере с
.map()
. Стрелочная функция, передаваемая .filter()
, использует лишь один параметр, поэтому мы обходимся без круглых скобок. Её тело содержит лишь одно выражение, поэтому его можно не заключать в фигурные скобки и допустимо обойтись без 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()
нуждается в коллбэке, принимающем два параметра. Это — аккумулятор и текущее значение. Аккумулятор — это первый параметр. Именно он модифицируется на каждой итерации и передаётся в следующую: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);
Итоги
Надеюсь, теперь вы разобрались с методами JS-массивов
.map()
, .filter()
и .reduce()
. Воспринимайте их как механизмы, улучшающие читабельность вашего кода. Они позволяют писать более компактные программы, чем те, которые получаются при использовании обычных циклов. Но самая главная их сильная сторона заключается в том, что они позволяют ясно выразить намерение, которое лежит в основе кода.Благодаря этим методам код, который написан довольно давно, будет легче читать. Вместо того, чтобы вникать в конструкции, помещённые внутрь циклов
for
, делая это лишь для того, чтобы понять их конечную цель, вы, лишь увидев имя одного из этих методов, уже сможете сформировать общее представление о причинах существования того или иного участка кода.Уважаемые читатели! Пользуетесь ли вы методами JS-массивов .map(), .filter() и .reduce()?