Уменьшение, сопоставление и фильтр — единственные функции, которые я обычно использую для работы с массивами, иногда с использованием for..of
, когда есть определенный асинхронный вариант использования. Я часто советую людям полностью забыть о существовании forEach
. Это замаскированный цикл for, который почти всегда приводит к изменению переменных, что затрудняет отладку кода.
Вы можете разбить его на три варианта использования, когда думаете о том, зачем вам нужно перебирать массив и манипулировать данными.
- Чтобы добавить в схему данных, мы можем использовать
map
. При работе с[Object]
вы можете обновлять свойства в каждом индексе. При работе с[string|number]
вы можете манипулировать каждым индексом - Чтобы отфильтровать значения из массива, мы можем использовать
filter
. Когда вам нужно подмножество данных из одного массива в другой на основе некоторых критериев. - Чтобы создать/изменить схему данных, мы можем использовать
reduce
. Подробнее об этом в этой статье.
Чаще всего я вижу, как разработчики правильно используют фильтр и карту (за исключением того, что карта нечетного времени используется в качестве замены для forEach
), в то время как reduce
практически не используется. Я надеюсь объяснить, где и когда использовать reduce
и почему в большинстве случаев это лучший вариант.
Уменьшить › forEach
Во-первых, давайте посмотрим, как я обычно вижу, как люди манипулируют массивом без использования reduce
.
type User = { name: string; email: string; role: string; } type Option = { label: string; value: User } const getUserDropdownOptions = (users: User) => { const userOptions: Option[] = []; users.forEach((user) => { userOptions.push({ label: `${user.name} <${user.email}>` value: user, }) }); return userOptions; }
В этом примере мы берем массив пользователей и создаем массив параметров для использования в раскрывающемся списке. Разбираем происходящее:
- Создайте возвращаемый массив с именем
userOptions
, чтобы поместить в него новую схему данныхOption
. - Прокрутите
users
и используйте данные для создания типа данныхOption
. - Возврат
userOptions
На этом примере вы можете посмотреть, как работает reduce
. Если мы переименуем userOptions
в acc
(сокращение от аккумулятора), этот код будет почти идентичен.
const getUserDropdownOptions = (users: User) => users.reduce<Option[]>((acc, user) => { acc.push({ label: `${user.name} <${user.email}>` value: user, }); return acc; }, [])
Этот код дает тот же результат, что и пример forEach
выше, за исключением использования сокращения. Давайте посмотрим на некоторые из ключевых отличий
- Поскольку
reduce
возвращаетOption[]
, нет необходимости в{}
обертке функции. Это кажется не очень важным в таком маленьком примере, как этот, но когда у вас есть несколько таких функций в файле, это помогает навести порядок. - Опять же, поскольку
reduce
уже возвращаетOption[]
, дополнительная переменнаяuserOptions
не нужна. Вместо этого вы можете использовать переменную-аккумулятор (acc
) в функции обратного вызова сокращения.
Некоторые из вас, знакомые с сокращением, могли заметить более компактный способ записи возврата из сокращения
users.reduce<Option[]>((acc, user) => [ ...acc, { label: `${user.name} <${user.email}>` value: user, } ], [])
Я перестал записывать свои отчеты таким образом, потому что при работе с большими структурами данных это оказывает более существенное влияние на производительность, чем вы думаете. Выполнение этого метода деконструкции в редукционном редукторе замедлило приложение, над которым я работал, на 500%, 50 мс -> 10 мс. Эта оптимизация кажется не такой уж большой, но когда вся функция сокращения выполняется за 500 мс, а оптимизаций такого размера несколько, вы переходите от медлительности к быстроте всего за несколько строк кода.
«Уменьшить» потребность в let
Если мне нужно написать let
в своем коде, это я меняю const
на let
переменной по определенной причине. Есть несколько причин использовать let
, а с reduce
в вашем наборе инструментов вы найдете еще меньше вариантов использования.
В тех случаях, когда у вас есть массив примитивов и вы хотите манипулировать ими, forEach
вам нужно будет использовать переменную let
для отслеживания вещей. Давайте посмотрим на добавление значений из массива
const arr = [1, 2, 3, 4]; const addArrayValues = (arr: number[]) => { let sum = 0; arr.forEach((num) => { sum = sum + num }) return sum }
У нас есть функция, которая принимает number[]
и возвращает сумму всех чисел в массиве. Поток очень похож на первый пример.
- Создайте возвращаемую переменную. На каждой итерации
sum
необходимо переназначать, поэтому мы должны использоватьlet
. - Перебрать массив и изменить это возвращаемое значение
- Вернуть эту переменную
Теперь устранить необходимость в let
здесь так же просто, как использовать reduce
const addArrayValues = (arr: number[]) => arr.reduce((sum, num) => sum + num, 0)
Написание такого кода не связано с игрой в гольф, иначе я бы назвал переменные a
s
и n
. Я говорю о написании чистого, неизменного кода, который легко понять, когда вы чувствуете себя комфортно с reduce
.
Мне трудно применять примеры из статей к моему коду, когда они настолько тривиальны. Вот более сложный пример подсчета, который мне приходилось делать раньше.
type User = { name: string; email: string; postCounts: { tiktok: number; twitter: number; } } type PostCount = { tiktok: number; total: number; twitter: number; } const defaultCount: PostCount = { tiktok: 0 total: 0, twitter: 0, } const getTotalPostCount = (users: User[]) => users.reduce<PostCount>((acc, user) => { const { postCounts: { tiktok, twitter } } = user const userTotal = tiktok + twitter; return { tiktok: acc.tiktok + tiktok, total: acc.total + userTotal, twitter: acc.twitter + twitter } }, defaultCount)
Мы отслеживаем итоги для каждой социальной платформы и общий итог. Использование сокращения здесь не позволяет нам иметь 3 переменные let
, которые в противном случае нам пришлось бы использовать, если бы они были записаны с forEach
.
Надеюсь, это придаст вам больше уверенности при попытке заменить ваши старые функции forEach
на reduce
. Начните с малого, окуните пальцы ног, прежде чем пытаться стать умнее, и напишите основные логические части с помощью reduce