Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос нашим менторам. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Агрегация (reduce)

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

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

const users = [
  { name: 'Igor', amount: 19 },
  { name: 'Danil', amount: 1 },
  { name: 'Vovan', amount: 4 },
  { name: 'Matvey', amount: 16 },
];

let sum = 0;
for (const user of users) {
    sum += user.amount;
}

console.log(sum); // => 40

Основное отличие агрегации от отображения и фильтрации в том, что результатом агрегации может быть любой тип данных — как примитивный так и составной, например, массив. Кроме того, агрегация нередко подразумевает инициализацию начальным значением, которое принято называть аккумулятором. В примере выше она выполняется на строчке let sum = 0. Здесь переменная sum "аккумулирует" результат внутри себя.

Посмотрим ещё один пример агрегации — группировку имён пользователей по возрасту:

import _ from 'lodash';

const users = [
  { name: 'Petr', age: 4 },
  { name: 'Igor', age: 19 },
  { name: 'Vovan', age: 4 },
  { name: 'Matvey', age: 16 },
];

const usersByAge = {};
for (const { age, name } of users) {
  // Функция _.has() проверяет наличие свойства в объекте,
  if (!_.has(usersByAge, age)) {
    usersByAge[age] = [];
  }
  usersByAge[age].push(name);
}

console.log(usersByAge);
// => { 4: [ 'Petr', 'Vovan' ], 16: [ 'Matvey' ], 19: [ 'Igor' ] }

В этом примере результатом агрегации становится объект, в свойствах которого записаны массивы. Этот результат в самом начале инициируется пустым объектом, а затем постепенно, на каждой итерации, "наполняется" нужными данными. Значение, которое накапливает результат агрегации, принято называть словом "аккумулятор". В примерах выше это sum и usersByAge.

Реализуем первый пример, используя reduce():

const sum = users.reduce((acc, user) => acc + user.amount, 0);
console.log(sum); // => 40

Метод reduce() принимает на вход два параметра — функцию-обработчик и начальное значение аккумулятора. Этот же аккумулятор возвращается наружу в качестве результата всей операции.

Функция, передаваемая в reduce() — самая важная часть и ключ к пониманию работы всего механизма агрегации. Она принимает на вход два значения. Первый — текущее значение аккумулятора, второй — текущий обрабатываемый элемент. Задача функции — вернуть новое значение аккумулятора. reduce() никак не анализирует содержимое аккумулятора. Всё, что он делает, передаёт его в каждый новый вызов до тех пор, пока не будет обработана вся коллекция, и в конце концов вернёт его наружу. Подчеркну, что возвращать аккумулятор надо всегда, даже если он не изменился.

Второй пример с использованием reduce() выглядит так:

import _ from 'lodash';

// предварительно подготовим функцию-обработчик
const cb = (acc, user) => {
  if (!_.has(acc, user.age)) {
    acc[user.age] = [];
  }
  acc[user.age].push(user.name);
  return acc; // обязательно вернуть!
};

// Начальное значение – пустой объект
const usersByAge = users.reduce(cb, {});

Код практически не изменился, за исключением того, что ушёл цикл и появился возврат аккумулятора из анонимной функции.

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

Реализация

Напишем свою собственную функцию myReduce(), работающую аналогично методу массива reduce():

const myReduce = (collection, callback, init) => {
  let acc = init; // инициализация аккумулятора
  for (const item of collection) {
    acc = callback(acc, item); // Заменяем старый аккумулятор новым
  }
  return acc;
};

const users = [
  { name: 'Petr', age: 4 },
  { name: 'Igor', age: 19 },
  { name: 'Vovan', age: 4 },
  { name: 'Matvey', age: 16 },
];

const oldest = myReduce(
  users,
  (acc, user) => (user.age > acc.age ? user : acc),
  users[0],
);
console.log(oldest); // => { name: 'Igor', age: 19 }

Дополнительные материалы

  1. Метод reduce
  2. Функция has из библиотеки Lodash

<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят менторы из команды Хекслета или другие студенты.

Для полного доступа к курсу нужна профессиональная подписка

Профессиональная подписка откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, даст возможность обращаться за помощью к менторам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Зарегистрироваться

или войти в аккаунт

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Отправляя форму, вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг». Защита от спама reCAPTCHA «Конфиденциальность» и «Условия использования».

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг». Защита от спама reCAPTCHA «Конфиденциальность» и «Условия использования».