15 Полезных javascript примеров map(), reduce() и filter()

Spread the love

В статье приведено 15 примеров использование в Javascript функций map(), reduce() и filter() . Когда вы читаете о Array.reduce и о том как это круто, вы часто можете встретить один и то же пример, показывающий использование этой функции. Этот пример демонстрирует суммирование списка чисел. Проблема с этим примером в том что, я никогда не видел использование этого примера в реальных проектах. Тем не менее, я часто вижу лишних 7–8 строк операторов цикла для решения довольно обычных задач, которые при использование Array.reduce всегда можно преобразовать в одну строку.

Недавно я прорефакторил несколько модулей с использованием этих замечательных функций и был шокирован тем, насколько удобнее стало читать код. Итак, ниже приведен список вкусностей. Также не стесняйтесь размещать свои примеры в разделе комментариев.

Давайте начнем!

1. Удаление дубликатов из массива числе/строк

Это единственный пример, который не относится в нашем обзоре, к map/reduce/filter, но он настолько компактный, что трудно было не поместить его в список. Кроме того, мы будем использовать похожий код в нескольких примерах.

let values = [3, 1, 3, 5, 2, 4, 4, 4];
let uniqueValues = [...new Set(values)];
// uniqueValues is [3, 1, 5, 2, 4]

2. Простой поиск (чувствительный к регистру)

Метод filter() создает новый массив со всеми элементами, которые проходят условие, реализованного предоставленной функцией.

let users = [
  { id: 11, name: 'Adam', age: 23, group: 'editor' },
  { id: 47, name: 'John', age: 28, group: 'admin' },
  { id: 85, name: 'William', age: 34, group: 'editor' },
  { id: 97, name: 'Oliver', age: 28, group: 'admin' }
];
let res = users.filter(it => it.name.includes('oli'));
// res is []

3. Простой поиск (не чувствительный к регистру)

let res = users.filter(it => new RegExp('oli', "i").test(it.name));
// res is
[
  { id: 97, name: 'Oliver', age: 28, group: 'admin' }
]

4. Проверка есть ли у пользователей права администратора

Метод some() проверяет, соответствует ли хотя бы один элемент в массиве условию, реализованному в предоставленной функцией.

let hasAdmin = users.some(user => user.group === 'admin');
// hasAdmin is true

5. Сглаживание массива массивов

Код решает задачу преобразования массива массивов в один плоский массив. Результат первой итерации будет равен: […[], …[1, 2, 3]] что означает, что он преобразуется в [1, 2, 3] – это значение мы предоставляем как ‘acc’ на второй итерации и так далее.

let nested = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
let flat = nested.reduce((acc, it) => [...acc, ...it], []);
// flat is [1, 2, 3, 4, 5, 6, 7, 8, 9]

Вероятно, это самая короткая реализация для создания массива (метод Array.flat все еще является экспериментальной функцией). Обратите внимание на то, что использование оператора spread внутри функции reduce не очень хорошо с точки зрения производительности. Этот пример того случая, когда производительностью можно пренебречь только если позволяет контекст использования ☝️

6. Создание объекта, который содержит частоту использования ключей

Давайте сгруппируем и посчитаем свойство ‘age’ для каждого элемента в массиве:

let users = [
  { id: 11, name: 'Adam', age: 23, group: 'editor' },
  { id: 47, name: 'John', age: 28, group: 'admin' },
  { id: 85, name: 'William', age: 34, group: 'editor' },
  { id: 97, name: 'Oliver', age: 28, group: 'admin' }
];
let groupByAge = users.reduce((acc, it) =>
  ({ ...acc, [it.age]: (acc[it.age] || 0) + 1 }),
{});
// groupByAge is {23: 1, 28: 2, 34: 1}

7. Индексирование массива объектов (таблица соответствий)

Вместо того, чтобы проходить через весь массив во время поиска пользователя по идентификатору, мы можем создать объект, в котором идентификатор пользователя представляет ключ (с постоянным временем поиска).

let uTable = users.reduce((acc, it) => ({...acc, [it.id]: it }), {})
// uTable equals:
{
  11: { id: 11, name: 'Adam', age: 23, group: 'editor' },
  47: { id: 47, name: 'John', age: 28, group: 'admin' },
  85: { id: 85, name: 'William', age: 34, group: 'editor' },
  97: { id: 97, name: 'Oliver', age: 28, group: 'admin' }
}

Это особенно полезно когда вам часто приходится обращаться к данным следующим образом uTable[85].name a lot.

8. Извлечение уникальных значений для определенного ключа для каждого элемента в массиве.

Давайте создадим список групп пользователей. Метод map() создаст новый массив с результатами вызова предоставленной функции для каждого элемента в вызывающем массиве.

let listOfUserGroups = [...new Set(users.map(it => it.group))];
// listOfUserGroups is ['editor', 'admin'];

9. Обратное мапирование объекта типа ключ/значение

let cities = {
  Lyon: 'France',
  Berlin: 'Germany',
  Paris: 'France'
};
let countries = Object.keys(cities).reduce(
  (acc, k) => (acc[cities[k]] = [...(acc[cities[k]] || []), k], acc) , {});
// countries is
{
  France: ["Lyon", "Paris"],
  Germany: ["Berlin"]
}

Этот однострочник выглядит довольно сложно. Мы используем оператор comma (запятая), что означает, что мы возвращаем последнее значение в скобках – acc. Давайте перепишем этот пример более production-ready способом:

let countries = Object.keys(cities).reduce((acc, k) => {
  let country = cities[k];
  acc[country] = [...(acc[country] || []), k];
  return acc;
}, {});

10. Создание массива значений Фаренгейта из массива значений Цельсия

Думайте об этом примере как о примере с обработкой каждого элемента массива заданной формулой 🤓

let celsius = [-15, -5, 0, 10, 16, 20, 24, 32]
let fahrenheit = celsius.map(t => t * 1.8 + 32);
// fahrenheit is [5, 23, 32, 50, 60.8, 68, 75.2, 89.6]

11. Кодировать объекта в строку запроса

let params = {lat: 45, lng: 6, alt: 1000};
let queryString = Object.entries(params).map(p => encodeURIComponent(p[0]) + '=' + encodeURIComponent(p[1])).join('&')
// queryString is "lat=45&lng=6&alt=1000"

12. Отображение таблицы пользователей в виде читаемой строки только с указанными ключами

В определенных случаях вам может понадобится отобразить массив объектов с выбранными ключами в виде строки, но без использования JSON.stringify . 😦Например из-за необходимость исключение определенных ключей.

let users = [
  { id: 11, name: 'Adam', age: 23, group: 'editor' },
  { id: 47, name: 'John', age: 28, group: 'admin' },
  { id: 85, name: 'William', age: 34, group: 'editor' },
  { id: 97, name: 'Oliver', age: 28, group: 'admin' }
];
users.map(({id, age, group}) => `\n${id} ${age} ${group}`).join('')
// it returns:
"
11 23 editor
47 28 admin
85 34 editor
97 28 admin"

13. Поиск и замена пары ключ-значение в массиве объектов

Допустим нам понадобилось изменить возраст Джона. Если мы будем знать индекс, мы можем легко это сделать с помощью: users[1].age = 29. Однако у нас есть и другая возможность это сделать:

let updatedUsers = users.map(
  p => p.id !== 47 ? p : {...p, age: p.age + 1}
);
// John is turning 29 now

Здесь вместо того, чтобы изменять один элемент в нашем массиве, мы создаем новый массив с отличием только в одном элементе. Теперь мы можем сравнивать наши массивы только по ссылкам updatedUsers == users, что очень быстро работает! React.js использует такой же подход для ускорения процесса согласования. Подробнее об этом можно почитать здесь.

14. Объединение (A ∪ B) массивов

С меньшим кодом чем использование метода lodash union.

let arrA = [1, 4, 3, 2];
let arrB = [5, 2, 6, 7, 1];
[...new Set([...arrA, ...arrB])]; // returns [1, 4, 3, 2, 5, 6, 7]

15. Поиск пересечений в массивах (A ∩ B)

Это последний пример!

let arrA = [1, 4, 3, 2];
let arrB = [5, 2, 6, 7, 1];
arrA.filter(it => arrB.includes(it)); // returns [1, 2]

В качестве упражнения можете попытаться реализовать поиск разницы в массивах (A \ B). Подсказка: используйте восклицательный знак.

Завершение!

Надеюсь моя статься окажется вам полезной. Если у вас есть какие-либо вопросы или пожелания, дайте мне знать в комментариях ниже.

Оригинал

Была ли вам полезна эта статья?
[33 / 4.8]

Spread the love
Подписаться
Уведомление о
guest
4 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Анонимно
Анонимно
4 лет назад

привет. помоги решить задачку. вот задача
Реализуйте и экспортируйте по умолчанию функцию, которая переводит входные данные в удобный для построения графика формат.

На вход эта функция принимает массив данных. Каждая запись массива представляет из себя объект типа { value: 14, date: ‘02.08.2018’ }. Например:

const data = [
{ value: 14, date: ‘02.08.2018’ },
{ value: 43, date: ‘03.08.2018’ },
{ value: 38, date: ‘05.08.2018’ },
];
Вторым и третьим параметрами функция принимает даты (в форме строк типа ‘YYYY-MM-DD’) начала и конца периода:

const begin = ‘2018-08-01’;
const end = ‘2018-08-06’;
Диапазон дат задаёт размер выходного массива, который должна сгенерировать реализуемая функция. Правила формирования итогового массива:

он заполняется записями по всем дням из диапазона begin – end
в него включаются только те записи из входного массива, даты которых попадают в диапазон
если во входном массиве нет данных для какого-то дня из диапазона, то в свойство value записи этого дня установить значение 0
const result = normalizeData(data, beginDate, endDate);

console.log(result);
// OUTPUT
[ { value: 0, date: ‘01.08.2018’ },
{ value: 14, date: ‘02.08.2018’ },
{ value: 43, date: ‘03.08.2018’ },
{ value: 0, date: ‘04.08.2018’ },
{ value: 38, date: ‘05.08.2018’ },
{ value: 0, date: ‘06.08.2018’ } ]
Подсказки
Обратите внимание, что в практике импортированы функции для работы c датами, коллекциями и объектами. При необходимости вы можете (это необязательное требование) ими воспользоваться.
Даты: https://date-fns.org/

аноним
аноним
4 лет назад

вот начало моего решения
const normalizeData = (date, begin, end) => {

const eachDay = eachDayOfInterval({start: new Date(begin), end: new Date(end)});
const formatData = eachDay.map(e => format(e, ‘dd.MM.yyyy’));

}

Макс
Макс
4 лет назад

Отличная статья!!!

aika
aika
1 год назад

всем привет можете помочь с одной задачкой
Напишите функцию, которая получает массив функций и начальное значение, как аргументы. Функция должна вернуть результат выполнения всех функций в массиве поочередно, используя начальное значение. Используйте reduce.
Например
[
(a) => a+ 1,
(a) => a * 2
],
5
5 начальное значение.
Сначала запускается функция с ((a) => a+ 1)(5). Результат будет 6. теперь 6 используется во второй функции
((a) => a * 2)(6)
Итог 12
Количество функций в начальном массиве не ограничено
Чтобы решить эту задачу – четко решите, что будет являться аккумулятором, что будет являться ресивером в редьюсе. Какие у них типы. Как пройдет редьюс в первом шаге и что он вернет.
Задача решается в одну строчку, но тяжела для понимания. Ключ решения – ясность.
Input: [ (a) => a+ 1, (a) => a * 2 ], 5
Actions:
Output: 12
Input: [ (a) => a – 1, (a) => a * 2 ], 5
Actions:
Output: 8
Input: [ (a) => a + 2, (a) => a * 5, (a) => a – 5 ], 3
Actions:
Output: 20