Свяжитесь с нами
Спасибо

Мы получили заявку и свяжемся с вами в ближайшее время

ВЕРНУТЬСЯ НА ГЛАВНУЮ
Свяжитесь с нами
Ошибка

Не удалось отправить заявку, повторите позже

ПОПРОБОВАТЬ ЕЩЁ РАЗ
Неочевидно, но факт: 5 способов...
ВРЕМЯ ЧТЕНИЯ
1009

Неочевидно, но факт: 5 способов использовать метод reduce в JS

разработчик департамента e-commerce ГК «КОРУС Консалтинг»


Современный стандарт JavaScript предоставляет множество методов для «умного» перебора массивов. Среди наиболее популярных — forEach, for или for…of. Если же необходимо перебрать массив и вернуть данные для каждого элемента, подойдёт map.

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

В каких сценариях его можно применять — в этой статье.


Reduce сворачивает массив к одному значению (редуцирует). Этим значением может быть любой тип данных — как примитивный, так и составной, например, массив.


Метод принимает два аргумента:

  1. callback-функцию, выполняемую для запуска каждого элемента в массиве,
  2. начальное значение initialValue.

Callback также принимает два аргумента: accumulator, который возвращает функция callback после посещения очередного элемента, и текущий элемент в цикле currentValue.

Рассмотрим несколько неочевидных применений метода reduce, которые помогут придерживаться принципа write less, do more.

Минимальное и максимальное значение в массиве

Для того, чтобы найти минимальное и максимальное значение, можно использовать Math API, например:

const array = [-9, 18, 6, 5];
const max = Math.max(...array); // 18
const min = Math.min(...array); // -9


Но при использовании больших массивов (~10^7) можно столкнуться с ошибками. Исключить эти проблемы и при этом получить нужный результат можно с помощью метода reduce.

const array = [-9, 18, 6, 5];
const max = array.reduce((max, num) => (max > num ? max : num)); // 18
const min = array.reduce((min, num) => (min < num ? min : num)); // -9


Может потребоваться разобрать в URL адресе GET параметры и сложить их в объект.

const parseQuery = () => {
  const url = new  URL("https://somedomain.dev?name=hello&amp;amp;amp;amp;amp;amp;amp;title=world&amp;amp;am....;);
 
const search = url.search;
 
let query = {};
  search
    .slice(
1)
    .split(
"&")
    .forEach((item) => {
     
const [key, value] = item.split("=");
      query[key] =
decodeURIComponent(value);
    });
 
return query;
};

// {name: "hello", title: "world"}


Но с использованием метода reduce все становится понятнее и предсказуемее:

const parseQuery = () => {
const url = new URL("https://somedomain.dev?name=hello&amp;amp;title=world&amp;quot;);
const search = url.search;
return search
    .replace(
/(^\?)|(&$)/g, "")
    .split(
"&")
    .reduce((query, item) => {
     
const [key, value] = item.split("=");
      query[key] =
decodeURIComponent(value);
     
return query;
    }, {});
};

// {name: "hello", title: "world"}


Вот как это работает:

1. Получаем search параметры из URL — https://somedomain.dev?name=hello&title=world

const search = url.search; // ?name=hello&title=world


2. Удаляем лишние символы

search.replace(/(^\?)|(&$)/g, "");
//?name=hello&title=world => name=hello&title=world


3. Используем reduce, чтобы собрать параметры в объект.

Десериализация параметров

В этом примере ситуация противоположная. Если нужно к ссылке добавить набор параметров, то использовать конкатенацию (склеивание строк) не очень удобно, особенно если таких параметров десятки или сотни. Из-за этого сильно падает читаемость кода, размер строки растёт с каждыми новым параметром, что создает сложности для дальнейшей поддержки.

const searchObj = { name: "hello", title:"world"}; // many others parameters
const link = `https://somedomain.dev?name=${searchObj.name}&age=${searchObj.title}`;


Поправить ситуацию можно с помощью метода reduce. Для начала пишем функцию:

const stringifySearch = (search = {}) => {
 
return Object.entries(search)
   .reduce(
     (t, v) =>
`${t}${v[0]}=${encodeURIComponent(v[1])}&`,
    
Object.keys(search).length ? "?" : ""
   )
   .replace(
/&$/, "");
};


Далее берем объект из ранее описанного примера и применим ее:

const searchObj = { name: "hello", title:"world"}; // many others parameters
const search = stringifySearch(searchObj);
const link = `https://somedomain.dev${search}`; // https://somedomain.dev?name=hello&amp;title=world


Теперь можно не переживать за то, что при изменении объекта последующий код не будет работать корректно.

Выбор уникальных элементов массива

Тут все просто: идем по массиву и ищем, есть ли уже в нем такое значение. Если нет, добавляем его, а если есть — идем дальше. Таким образом, на выходе будет массив только уникальных значений.

const someArray = [ 1, 2, 1, 2, -1, 10, 11 ]
const uniqueArray = someArray.reduce((acc, item) => acc.includes(item) ? acc : [ ...acc, item ], []) // [1, 2, -1, 10, 11]


Определение количества одинаковых элементов массива

Чтобы получить количество каждого элемента в массиве, дополнительно используем Map — это коллекция ключ-значение, как и Object.

Основное отличие в том, что в Map мы сможем работать с массивом, в котором используются значения разных типов (числа/строки).

const count = (array) => {
 
return array.reduce((acc, item) => (acc.set(item, (acc.get(item) || 0) + 1), acc), new Map())
}
const array = [ 1, 2, 1, 2, -1, 0, 0, 10, 10, 6 ]
console.log(count(array)) // Map(6) {1 => 2, 2 => 2, -1 => 1, 0 => 2, 10 => 2, 6 => 1}

Получение нескольких свойств объекта

Подобный сценарий довольно часто возникает в повседневной работе. Давайте посмотрим пример.

У нас есть объект с множеством свойств:

const obj = {
 
a: 1,
 
b: 2,
 
c: 3,
 
d: 4,
 
e: 5
 
// ...
}


 Мы хотим получить некоторые свойства предыдущего объекта и создать новый:

const newObj = {
 
a: obj.a,
 
b: obj.b,
 
c: obj.c,
 
d: obj.d
 
// ...
}


Кажется, так делать не очень эффективно, не правда ли?

Напишем небольшой хелпер, который упростит нам решение проблемы:

const getObjectKeys = (obj = {}, keys = []) => {
 
return Object.keys(obj).reduce((acc, key) => (keys.includes(key) && (acc[key] = obj[key]), acc), {});
}


Ну и, конечно, проверим, как все это работает:

const obj = {
 
a: 1,
 
b: 2,
 
c: 3,
 
d: 4,
 
e: 5
 
// ...
}
const newObj = getObjectKeys(obj, [ 'a', 'b', 'c', 'd' ])
console.log(newObj) // {a: 1, b: 2, c: 3, d: 4}


Это лишь часть возможных сценариев применения метода в повседневных задачах. Надеюсь, вы нашли для себя что-то интересное.

Благодарю за внимание и happy coding!


Закажите бесплатную консультацию эксперта


Опубликовано на Tproger

Подпишитесь на наши обновления

Раз в месяц присылаем полезные материалы и новые статьи из блога.

Некорректно заполнен e-mail

Читайте также

Фреймворк: что это такое и как выбрать подходящий для фронтенда и бэкенда ~ 10 мин. 13 июля 2022
ТЕХНОЛОГИИ
КОРУС Консалтинг
DevOps: как крупному бизнесу повысить эффективность разработки ИТ-продуктов ~16 мин. 27 января 2022
E-COMMERCEСТРАТЕГИЯТЕХНОЛОГИИ
Сергей Рабинович
Как продать Agile заказчику и избежать очевидных проблем ~ 5 мин. 13 июля 2021
E-COMMERCEТРЕНДЫ
Анастасия Гусакова
Подпишитесь на наши обновления

Раз в месяц присылаем полезные материалы и новые статьи из блога.

Некорректно заполнен e-mail


наверх
ЧИТАЙТЕ НАС В TELEGRAM
еКОМната — нишевое медиа о e‑commerce и B2B. Только экспертиза, цифры и кейсы
ПОДПИСАТЬСЯ