Содержание
Данный метод наверное наиболее сложный из методов массивов. Давайте рассмотрим как с ним работать и разберем несколько примеров где он может применяться.
Метод reduce()
выполняет заданную функцию к каждому элементу массива и возвращает «аккумулированный» результат.
array.reduce(function(total, currentValue, currentIndex, arr))
total
— начальное значение или ранее возвращенное значение (из предыдущей итерации).currentValue
— текущее значение массиваcurrentIndex
— индекс текущего элементаarr
— проходимый массив
Примеры использования
Наиболее наглядный пример — это подсчет суммы значений элементов массива. Например, у нас есть такой массив:
const salary = [1000, 1200, 900, 1800]; const sum = salary.reduce((total, currentItem) => { total += currentItem; return total; }, 0) // 0 - это начальное значение, тут мы могли бы указать другое число console.log('sum', sum); // 4900
Рассмотрим другой пример. У нас есть массив пользователей. Каждый пользователь это обьект. У каждого пользователя есть имя, возраст и размер компенсации. Нам нужно высчитать среднюю заработную плату:
const users = [ { name: 'John', age: 32, salary: 1500 }, { name: 'Mike', age: 25, salary: 1200 }, { name: 'Pieter', age: 27, salary: 1400 }, { name: 'Jim', age: 22, salary: 1800 } ]; const averageSalary = users.reduce((total, user) => { total += user.salary/users.length; return total; }, 0); console.log('Average Salary', averageSalary); // 1475
Если в примере выше у вас users это не массив, а объект и задача та же — посчитать среднюю з/п, тогда мы можем сделать так:
const users = { 0: { name: 'John', age: 32, salary: 1500 }, 1: { name: 'Mike', age: 25, salary: 1200 }, 2: { name: 'Pieter', age: 27, salary: 1400 }, 3: { name: 'Jim', age: 22, salary: 1800 } } // Определяем количество элементов в объекте const userObjLength = Object.keys(users).length; const averageSalary = Object.keys(users).reduce((total, key) => { // В [key] у нас попадает ключ с обьекта users (от 0 до 3) total += users[key].salary/userObjLength; return total; }, 0); console.log('Average Salary', averageSalary); // 1475
Преобразование массива в объект с помощью reduce
В двух последних примерах у нас есть массив users
и есть объект users
. А что если нам нужно преобразовать массив в объект? Для этого мы также можем воспользоваться методом reduce()
const users = [ { name: 'John', age: 32, salary: 1500 }, { name: 'Mike', age: 25, salary: 1200 }, { name: 'Pieter', age: 27, salary: 1400 }, { name: 'Jim', age: 22, salary: 1800 } ] const usersObj = users.reduce((acc, user, index) => { acc[index] = user return acc; // Начальное значение {} }, {}); console.log('New Object', usersObj);
Так как на выходе нам нужен объект, мы вторым параметром в метод reduce передаем пустой объект и дальше на каждой итерации добавляем ему новую пару «ключ — значение». В качестве ключа у нас выступает индекс текущего элемента в массиве.
Пишем свой метод reduce на JavaScript
Мы разберем два примера: простой и более сложный, в котором будут обрабатываться ошибки и проверяться условия.
Первый пример:
Array.prototype.customReduce = function(callback, result) { let i = 0; // Проверяем если кол-во аргументов меньше 2, значит не передано дефолтное значение if (arguments.length < 2) { i = 1; // В данном случае this указывает на итерируемый массив result = this[0] } for (; i < this.length; i++) { result = callback(result, this[i], i, this); } return result; }
Мы сразу добавили данный метод к прототипу массива, чтобы его можно было легко вызвать. Теперь проверим выдаст ли он нам такой же результат, как был в самом первом примере этого поста:
const salary = [1000, 1200, 900, 1800]; const sum = salary.customReduce((total, currentItem) => { total += currentItem; return total; }, 0); console.log('sum', sum); // 4900
Теперь давайте рассмотрим более сложный вариант с дополнительными проверками:
Array.prototype.customReduce = function(callBack, initialValue) { // Проверяем не пустой ли массив и задано ли дефолтное значение if (!this.length && initialValue === undefined) throw new TypeError('Массив пустой. Невозможно к нему применить данный метод'); // Если дефолтное значение задано, то присваиваем его к accumulator, который в итоге будем возвращать let accumulator = initialValue; let index = 0; if (initialValue === undefined) { // Если нет дефолтного значения, тогда accumulator будет присваиваться значение первого элемента массива accumulator = this[0]; index = 1; } // В цикле проходимся по нашему массиву и передаем наш callback, который нужно выполнить для каждого элемента массива for (; index < this.length; index++) { // Как мы помним метод reduce принимает 4 параметра (см начало поста) // Поэтому мы в наш callback передаем такие же 4 параметра accumulator = callBack(accumulator, this[index], index, this); } return accumulator; }
Если остались вопросы — пишите в комментарии.