Метод reduce() у масиві
reduce() це один із найпотужніших і найуніверсальніших методів роботи з масивами в JavaScript: він послідовно зводить (reduce) масив до одного значення. Цим значенням може бути число, рядок, об'єкт, новий масив чи будь-яка інша структура, тому reduce() закриває майже всі задачі агрегації даних.
Теорія
TL;DR
reduce()проходить масив зліва направо і накопичує результат в акумуляторі.- Колбек має сигнатуру
(accumulator, currentValue, index, array)і мусить повертати новий акумулятор. - Другий аргумент
initialValueзадає стартове значення і тип результату. - Без
initialValueпершим акумулятором стає нульовий елемент, а порожній масив даєTypeError. - Для простого перетворення елементів краще
map(), для відборуfilter(), аreduce()беріть тоді, коли потрібен один підсумок.
Швидкий приклад
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 10Покроково це виглядає так:
acc = 0(початкове значення)- 0 + 1 -> 1
- 1 + 2 -> 3
- 3 + 3 -> 6
- 6 + 4 -> 10
Синтаксис і параметри
const result = array.reduce((accumulator, currentValue, index, array) => {
return newAccumulatorValue;
}, initialValue);| Аргумент | Опис |
|---|---|
accumulator | накопичене значення, тобто те, що повернула попередня ітерація |
currentValue | поточний елемент масиву |
index | індекс поточного елемента |
array | сам масив, над яким викликано метод |
initialValue | початкове значення акумулятора, формально необов'язкове, але вказувати його дуже бажано |
Типові сценарії використання
Підрахунок кількості елементів за умовою. reduce() часто застосовують для агрегації: підрахунків, статистики, об'єднань.
const users = [
{ name: 'Tim', age: 25 },
{ name: 'Alex', age: 17 },
{ name: 'John', age: 30 }
];
const adultsCount = users.reduce((acc, user) => {
return user.age >= 18 ? acc + 1 : acc;
}, 0);
console.log(adultsCount); // 2Перетворення масиву на об'єкт. Дуже зручно, коли дані потрібно перекласти в іншу структуру, наприклад у словник для швидкого пошуку.
const fruits = ['apple', 'banana', 'cherry'];
const fruitMap = fruits.reduce((acc, fruit, index) => {
acc[index] = fruit;
return acc;
}, {});
console.log(fruitMap);
// { 0: 'apple', 1: 'banana', 2: 'cherry' }Згортання масиву об'єктів в одне число. Класика для кошика чи звіту.
const products = [
{ price: 10 },
{ price: 20 },
{ price: 30 }
];
const total = products.reduce((acc, item) => acc + item.price, 0);
console.log(total); // 60Початкове значення і порожній масив
Якщо initialValue не вказати, акумулятором на першій ітерації стає перший елемент масиву, а обхід починається з другого.
const arr = [1, 2, 3];
const sum = arr.reduce((acc, num) => acc + num);
console.log(sum); // 6Тут acc на першій ітерації дорівнює 1 (перший елемент), а currentValue дорівнює спочатку 2, потім 3.
Якщо масив порожній і початкового значення немає, метод кидає помилку:
[].reduce((acc, x) => acc + x); // TypeError: Reduce of empty array with no initial valueСаме тому початкове значення варто задавати завжди, особливо коли масив приходить із мережі і може виявитися порожнім.
Коли брати reduce(), а коли інші методи
| Мета | Чи підходить reduce() |
|---|---|
| Сума або добуток чисел | Так |
| Підрахунок статистики або кількості | Так |
| Перетворення масиву на об'єкт | Так |
| Конкатенація рядків або масивів | Так |
| Перетворення кожного елемента | Ні, краще map() |
| Відбір елементів | Ні, краще filter() |
Порівняння з сусідніми методами перебору:
| Метод | Що робить | Що повертає | Чи може перервати цикл |
|---|---|---|---|
forEach() | перебирає елементи | undefined | ні |
map() | перетворює елементи | новий масив | ні |
filter() | відбирає елементи | новий масив | ні |
find() | знаходить перший відповідний елемент | елемент або undefined | так |
reduce() | зводить увесь масив до одного значення | будь-який тип: число, рядок, об'єкт | так, через логіку в колбеку |
Формула для запам'ятовування: arr.reduce((acc, elem) => новеЗначення, початковеЗначення).
Типові помилки
1. Забутий return у тілі колбека. Фігурні дужки стрілочної функції скасовують неявне повернення, тож акумулятор стає undefined.
arr.reduce((acc, num) => { acc + num }, 0); // undefinedПравильно:
arr.reduce((acc, num) => { return acc + num }, 0);2. Невідповідний тип початкового значення. Якщо елементи є рядками, а акумулятор числом, спрацює конкатенація замість додавання.
['1', '2', '3'].reduce((acc, n) => acc + n, 0); // '0123', це рядокПравильно, з явним перетворенням:
['1', '2', '3'].reduce((acc, n) => acc + Number(n), 0); // 63. Мутація зовнішніх даних. Акумулятор-об'єкт можна змінювати, бо він створений усередині виклику, але змінювати вихідні елементи масиву не варто: це робить результат непередбачуваним.
4. reduce() там, де вистачило б map() або filter(). Такий код читається важче, ніж послідовність простих методів, і не дає жодного виграшу.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.