JavaScript

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
Editorial Team

View Comments

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

    На вход эта функция принимает массив данных. Каждая запись массива представляет из себя объект типа { 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/

  • вот начало моего решения
    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'));

    }

  • всем привет можете помочь с одной задачкой
    Напишите функцию, которая получает массив функций и начальное значение, как аргументы. Функция должна вернуть результат выполнения всех функций в массиве поочередно, используя начальное значение. Используйте 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

Recent Posts

Vue 3.4 Новая механика v-model компонента

Краткий перевод: https://vuejs.org/guide/components/v-model.html Основное использование​ v-model используется для реализации двусторонней привязки в компоненте. Начиная с Vue…

12 месяцев ago

Анонс Vue 3.4

Сегодня мы рады объявить о выпуске Vue 3.4 «🏀 Slam Dunk»! Этот выпуск включает в…

12 месяцев ago

Как принудительно пере-отобразить (re-render) компонент Vue

Vue.js — это универсальный и адаптируемый фреймворк. Благодаря своей отличительной архитектуре и системе реактивности Vue…

2 года ago

Проблемы с установкой сертификата на nginix

Недавно, у меня истек сертификат и пришлось заказывать новый и затем устанавливать на хостинг с…

2 года ago

Введение в JavaScript Temporal API

Каким бы ни было ваше мнение о JavaScript, но всем известно, что работа с датами…

2 года ago

Когда и как выбирать между медиа запросами и контейнерными запросами

Все, кто следит за последними событиями в мире адаптивного дизайна, согласятся, что введение контейнерных запросов…

2 года ago