Learn JavaScript

reduce в JavaScript: простые случаи (сумма и объект-счетчик)

reduce - метод массива, который “сворачивает” массив в одно значение. Это может быть число, строка, объект или даже новый массив. В простых случаях reduce удобно использовать для суммирования и построения объекта-счетчика.

Идея reduce

reduce идет по массиву и на каждом шаге обновляет накопитель (accumulator).

const nums = [1, 2, 3, 4];

const sum = nums.reduce((acc, value) => acc + value, 0);

console.log(sum); // 10

Здесь:

  • acc - накопитель
  • value - текущий элемент
  • 0 - initialValue (начальное значение накопителя)

Синтаксис

array.reduce((acc, value, index, array) => {
  return nextAcc;
}, initialValue);
  • acc - накопитель
  • value - текущий элемент
  • index, array - дополнительные параметры (не всегда нужны)
  • initialValue - начальное значение накопителя

Практичное правило: initialValue лучше задавать явно.

Пример 1: сумма чисел

const nums = [10, 20, 30];

const sum = nums.reduce((acc, n) => acc + n, 0);
console.log(sum); // 60

Пример 2: произведение

const nums = [2, 3, 4];

const product = nums.reduce((acc, n) => acc * n, 1);
console.log(product); // 24

Начальное значение 1 важно: умножение на 1 не меняет результат.

Пример 3: максимум

const nums = [5, 7, 2, 10, 3];

const max = nums.reduce((acc, n) => (n > acc ? n : acc), -Infinity);
console.log(max); // 10

Пример 4: объект-счетчик

Частая задача - посчитать, сколько раз встречается каждое значение.

const letters = ["a", "b", "a", "c", "b", "a"];

const counts = letters.reduce((acc, letter) => {
  const current = acc[letter] || 0;
  acc[letter] = current + 1;
  return acc;
}, {});

console.log(counts); // { a: 3, b: 2, c: 1 }

Здесь накопитель - объект {}.

Объект-счетчик с числами

const nums = [1, 2, 2, 3, 3, 3];

const counts = nums.reduce((acc, n) => {
  const key = String(n);
  acc[key] = (acc[key] || 0) + 1;
  return acc;
}, {});

console.log(counts); // { "1": 1, "2": 2, "3": 3 }

Ключи объекта - строки, поэтому String(n) делает намерение явным.

Почему initialValue важно

Если initialValue не передать, reduce возьмет первый элемент массива как начальный acc, а перебор начнется со второго элемента. Это может быть неожиданно и ломает код на пустых массивах.

Пример, который выглядит нормально, но опасен:

const nums = [10, 20, 30];

const sum = nums.reduce((acc, n) => acc + n);
console.log(sum); // 60

Если массив пустой, будет ошибка:

const nums = [];

try {
  const sum = nums.reduce((acc, n) => acc + n);
  console.log(sum);
} catch (e) {
  console.log(e.name); // "TypeError"
}

С initialValue пустой массив обрабатывается корректно:

const nums = [];

const sum = nums.reduce((acc, n) => acc + n, 0);
console.log(sum); // 0

reduce и читаемость

reduce мощный, но его легко сделать слишком сложным. Практичное правило: использовать reduce, когда логика действительно “сводит к одному значению”. Если нужно просто преобразовать или отобрать элементы, обычно подходят map и filter.

Частые ошибки

1) Забыли вернуть acc

Колбэк reduce должен возвращать обновленный накопитель.

const nums = [1, 2, 3];

const sum = nums.reduce((acc, n) => {
  acc + n;
  return acc;
}, 0);

console.log(sum); // 0

Правильно:

const nums = [1, 2, 3];

const sum = nums.reduce((acc, n) => acc + n, 0);
console.log(sum); // 6

2) Неподходящее initialValue

Для суммы - 0, для произведения - 1, для счетчика - {}.

const nums = [2, 3];

const product = nums.reduce((acc, n) => acc * n, 0);
console.log(product); // 0

Что стоит запомнить

  • reduce сводит массив к одному значению.
  • Накопитель (acc) обновляется на каждом шаге и возвращается из колбэка.
  • initialValue лучше задавать явно: это делает код стабильнее и понятнее.
  • Простейшие кейсы: сумма, произведение, максимум, объект-счетчик.