Скажем, я хочу, чтобы сумма a.x
для каждого элемента в arr
.
arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN
У меня есть основания полагать, что топор в какой-то момент не определен.
Следующее работает отлично
arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7
Что я делаю не так в первом примере?
arr.reduce(function(a,b){return a + b})
в виду во втором примере.Ответы:
После первой итерации вы возвращаете число, а затем пытаетесь получить его свойство
x
для добавления к следующему объекту, в котором естьundefined
математическиеundefined
результатыNaN
.попробуйте вернуть объект, содержащий
x
свойство с суммой свойств x параметров:Объяснение добавлено из комментариев:
Возвращаемое значение каждой итерации
[].reduce
используется какa
переменная в следующей итерации.Итерация 1:
a = {x:1}
,b = {x:2}
,{x: 3}
назначаетсяa
в Итерация 2Итерация 2:
a = {x:3}
,b = {x:4}
.Проблема с вашим примером в том, что вы возвращаете числовой литерал.
Итерация 1:
a = {x:1}
,b = {x:2}
,// returns 3
аa
в следующей итерацииИтерации 2:
a = 3
,b = {x:2}
возвращаетNaN
Ряд буквальный
3
не ( как правило) имеет свойстваx
так этоundefined
иundefined + b.x
возвращается ,NaN
иNaN + <anything>
всегдаNaN
Пояснение : я предпочитаю свой метод перед другим топовым ответом в этой теме, поскольку я не согласен с идеей, что передача необязательного параметра для уменьшения с помощью магического числа для получения примитива числа является более чистой. Это может привести к меньшему количеству написанных строк, но, на мой взгляд, оно менее читабельно
источник
x
ключ для его работы.Более чистый способ сделать это - предоставить начальное значение:
Когда анонимная функция вызывается в первый раз, она вызывается
(0, {x: 1})
и возвращает0 + 1 = 1
. В следующий раз он вызывается с(1, {x: 2})
и возвращается1 + 2 = 3
. Затем он вызывается с(3, {x: 4})
, наконец, возвращаясь7
.источник
reduce
.TL; DR, установить начальное значение
Использование деструктуризации
arr.reduce( ( sum, { x } ) => sum + x , 0)
Без разрушения
arr.reduce( ( sum , cur ) => sum + cur.x , 0)
С машинописным шрифтом
arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)
Давайте попробуем метод деструктуризации:
Ключом к этому является установка начального значения. Возвращаемое значение становится первым параметром следующей итерации.
Техника, используемая в верхнем ответе, не идиоматична
В принятом ответе предлагается НЕ передавать «необязательное» значение. Это неправильно, так как идиоматический способ заключается в том, что второй параметр всегда включается. Зачем? Три причины:
1. Опасно - Не передавать начальное значение опасно и может создать побочные эффекты и мутации, если функция обратного вызова небрежна.
Вот
Если, однако, мы сделали это таким образом, с начальным значением:
Для записи, если вы не собираетесь изменять исходный объект, установите для первого параметра
Object.assign
пустой объект. Как это:Object.assign({}, a, b, c)
.2 - лучший вывод типа - при использовании инструмента, такого как Typescript, или редактора, такого как VS Code, вы получаете преимущество, сообщая компилятору исходный код, и он может отлавливать ошибки, если вы делаете это неправильно. Если вы не установите начальное значение, во многих ситуациях его невозможно будет угадать, что может привести к жутким ошибкам во время выполнения.
3 - Уважение к функторам - JavaScript светит лучше, когда его внутренний функциональный дочерний элемент освобожден. В функциональном мире существует стандарт того, как вы «складываете» или
reduce
массив. Когда вы складываете или применяете катаморфизм к массиву, вы берете значения этого массива для создания нового типа. Вам нужно сообщить результирующий тип - вы должны сделать это, даже если последний тип - это тип значений в массиве, другом массиве или любом другом типе.Давайте подумаем об этом по-другому. В JavaScript функции могут передаваться как данные, как работают обратные вызовы, каков результат следующего кода?
[1,2,3].reduce(callback)
Будет ли возвращать номер? Объект? Это проясняет
[1,2,3].reduce(callback,0)
Подробнее о спецификации функционального программирования читайте здесь: https://github.com/fantasyland/fantasy-land#foldable
Еще немного фона
reduce
Метод принимает два параметра,callback
Функция принимает следующие параметрыДля первой итерации
Если
initialItem
указано,reduce
функция передаетinitialItem
какaccumulator
и первый элемент массива какitemInArray
.Если
initialItem
это не предусмотрено, тоreduce
функция передает первый элемент в массив в качествеinitialItem
и второго элемента массива , как ,itemInArray
которые могут ввести в заблуждение поведение.Я учу и рекомендую всегда устанавливать начальное значение снижения.
Вы можете ознакомиться с документацией по адресу:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Надеюсь это поможет!
источник
Другие ответили на этот вопрос, но я решил бросить другой подход. Вместо того, чтобы перейти непосредственно к суммированию топора, вы можете объединить карту (от топора до x) и уменьшить (чтобы добавить x):
По общему признанию, это, вероятно, будет немного медленнее, но я думал, что стоит упомянуть это как выбор.
источник
Чтобы формализовать то, что было указано, редуктор представляет собой катаморфизм, который принимает два аргумента, которые могут совпадать с одним типом, и возвращает тип, который соответствует первому аргументу.
Это означает, что тело редуктора должно быть преобразовано,
currentValue
а текущее значение вaccumulator
значение новогоaccumulator
.При добавлении это работает прямым образом, поскольку значения аккумулятора и элемента оказываются одного типа (но служат разным целям).
Это просто работает, потому что они все числа.
Теперь мы даем начальное значение агрегатору. Начальным значением должен быть тип, который, как вы ожидаете, будет агрегатором (тип, который вы ожидаете получить в качестве конечного значения), в подавляющем большинстве случаев. Хотя вы не обязаны это делать (и не должны этого делать), важно помнить.
Узнав об этом, вы можете написать значимые сокращения для других проблем отношений n: 1.
Удаление повторяющихся слов:
Предоставление подсчета всех найденных слов:
Сокращение массива массивов до единого плоского массива:
Каждый раз, когда вы хотите перейти от множества вещей к одному значению, которое не соответствует 1: 1, уменьшение - это то, что вы могли бы рассмотреть.
Фактически, карта и фильтр могут быть реализованы как сокращения:
Я надеюсь, что это дает некоторый дополнительный контекст для использования
reduce
.Единственное дополнение к этому, которое я еще не взломал, - это когда ожидается, что типы ввода и вывода определенно должны быть динамическими, потому что элементы массива являются функциями:
источник
Для первой итерации 'a' будет первым объектом в массиве, следовательно, ax + bx вернет 1 + 2, т. Е. 3. Теперь в следующей итерации возвращенные 3 присваиваются a, поэтому a является числом, теперь n вызывающим ax даст NaN.
Простое решение - сначала отобразить числа в массиве, а затем уменьшить их, как показано ниже:
здесь
arr.map(a=>a.x)
предоставим массив чисел [1,2,4], теперь используя.reduce(function(a,b){return a+b})
просто добавим эти числа без каких-либо проблемДругое простое решение - просто предоставить начальную сумму как ноль, присваивая 0 'a', как показано ниже:
источник
На каждом шаге сокращения вы не возвращаете новый
{x:???}
объект. Так что вам либо нужно сделать:или вам нужно сделать
источник
На первом шаге он будет работать нормально, так как значение
a
будет равно 1, а значение ofb
будет равно 2, но как 2 + 1 будет возвращено, а на следующем шаге значениеb
будет возвращаемым значениемfrom step 1 i.e 3
и поэтомуb.x
будет неопределенным. .and undefined + anyNumber будет NaN, и именно поэтому вы получаете этот результат.Вместо этого вы можете попробовать это, задав начальное значение как ноль, т.е.
источник
Если у вас есть сложный объект с большим количеством данных, например, массив объектов, вы можете сделать шаг за шагом, чтобы решить эту проблему.
Например:
Во-первых, вы должны отобразить ваш массив в новый интересующий вас массив, в этом примере это может быть новый массив значений.
Эта функция обратного вызова вернет новый массив, содержащий только значения из исходного массива, и сохранит его в значениях const. Теперь ваши значения const - это массив вроде этого:
И теперь вы готовы выполнить ваше сокращение:
Как видите, метод Reduce выполняет функцию обратного вызова несколько раз. За каждый раз он принимает текущее значение элемента в массиве и суммирует с аккумулятором. Поэтому для правильного суммирования вам нужно установить начальное значение вашего аккумулятора как второй аргумент метода Reduce.
Теперь у вас есть новая постоянная сумма со значением 30.
источник
Функция Reduce перебирает коллекцию
переводится как:
Чтобы понять входы «уменьшить» функцию, я бы посоветовал вам взглянуть на исходный код этой функции. Библиотека Lodash имеет функцию Reduce, которая работает точно так же, как функция «Reduce» в ES6.
Вот ссылка: уменьшить исходный код
источник
вернуть сумму всех
x
реквизитов:источник
Вы можете использовать карту и уменьшить функции
источник
Функция уменьшения массива принимает три параметра: initialValue (по умолчанию 0), аккумулятор и текущее значение. По умолчанию значение initialValue будет равно «0». который забирается аккумулятором
Давайте посмотрим это в коде.
Теперь тот же пример с начальным значением:
То же самое относится и к массивам объектов (текущий вопрос stackoverflow):
Значение ONE ONE IN MINE равно InitialValue по умолчанию равно 0, и ему можно дать все, что я имею в виду {}, [] и число
источник
источник
вы не должны использовать топор для аккумулятора, вместо этого вы можете сделать это `arr = [{x: 1}, {x: 2}, {x: 4}]
arr.reduce (function (a, b) {a + bx}, 0) `
источник