JavaScript Map, Reduce, and Filter — JS Array Functions Explained with Code Examples
Map, reduce, and filter are all array methods in JavaScript. Each one will iterate over an array and perform a transformation or computation. Each will return a new array based on the result of the function. In this article, you will learn why and how to use each one.
Here is a fun summary by Steven Luscher:
Map
The map() method is used for creating a new array from an existing one, applying a function to each one of the elements of the first array.
Syntax
var new_array = arr.map(function callback(element, index, array) < // Return value for new_array >[, thisArg])
In the callback, only the array element is required. Usually some action is performed on the value and then a new value is returned.
Example
In the following example, each number in an array is doubled.
const numbers = [1, 2, 3, 4]; const doubled = numbers.map(item => item * 2); console.log(doubled); // [2, 4, 6, 8]
Filter
The filter() method takes each element in an array and it applies a conditional statement against it. If this conditional returns true, the element gets pushed to the output array. If the condition returns false, the element does not get pushed to the output array.
Syntax
var new_array = arr.filter(function callback(element, index, array) < // Return true or false >[, thisArg])
The syntax for filter is similar to map , except the callback function should return true to keep the element, or false otherwise. In the callback, only the element is required.
Examples
In the following example, odd numbers are «filtered» out, leaving only even numbers.
const numbers = [1, 2, 3, 4]; const evens = numbers.filter(item => item % 2 === 0); console.log(evens); // [2, 4]
In the next example, filter() is used to get all the students whose grades are greater than or equal to 90.
const students = [ < name: 'Quincy', grade: 96 >, < name: 'Jason', grade: 84 >, < name: 'Alexis', grade: 100 >, < name: 'Sam', grade: 65 >, < name: 'Katie', grade: 90 >]; const studentGrades = students.filter(student => student.grade >= 90); return studentGrades; // [ < name: 'Quincy', grade: 96 >, < name: 'Alexis', grade: 100 >, < name: 'Katie', grade: 90 >]
Reduce
The reduce() method reduces an array of values down to just one value. To get the output value, it runs a reducer function on each element of the array.
Syntax
arr.reduce(callback[, initialValue])
The callback argument is a function that will be called once for every item in the array. This function takes four arguments, but often only the first two are used.
- accumulator — the returned value of the previous iteration
- currentValue — the current item in the array
- index — the index of the current item
- array — the original array on which reduce was called
- The initialValue argument is optional. If provided, it will be used as the initial accumulator value in the first call to the callback function.
Examples
The following example adds every number together in an array of numbers.
const numbers = [1, 2, 3, 4]; const sum = numbers.reduce(function (result, item) < return result + item; >, 0); console.log(sum); // 10
In the next example, reduce() is used to transform an array of strings into a single object that shows how many times each string appears in the array. Notice this call to reduce passes an empty object <> as the initialValue parameter. This will be used as the initial value of the accumulator (the first argument) passed to the callback function.
var pets = ['dog', 'chicken', 'cat', 'dog', 'chicken', 'chicken', 'rabbit']; var petCounts = pets.reduce(function(obj, pet) < if (!obj[pet]) < obj[pet] = 1; >else < obj[pet]++; >return obj; >, <>); console.log(petCounts); /* Output: < dog: 2, chicken: 3, cat: 1, rabbit: 1 >*/
Video Explanation
Check out the video below from the freeCodeCamp.org YouTube channel. It covers the array methods discussed, plus a few more.
Начало работы с методами JavaScript-массивов .map(), .filter() и .reduce()
Когда я разбирался в том, как пользоваться методами JS-массивов .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
Вот как сделать то же самое с помощью .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
В отличие от методов .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()?