Уменьшение Javascript на массиве объектов

Скажем, я хочу суммировать a.x для каждого элемента в arr.

arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN

У меня есть причина полагать, что a.x undefined в какой-то момент.

Следующее работает отлично

arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7

Что я делаю неправильно в первом примере?

Ответ 1

После первой итерации вы возвращаете число, а затем пытаетесь получить свойство x для добавления к следующему объекту, который равен undefined, а математика с участием undefined приводит к NaN.

попробуйте вернуть объект, содержащий свойство x с суммой свойств x параметров:

var arr = [{x:1},{x:2},{x:4}];

arr.reduce(function (a, b) {
  return {x: a.x + b.x}; // returns object with property x
})

// ES6
arr.reduce((a, b) => ({x: a.x + b.x}));

// -> {x: 7}

Объяснение добавлено из комментариев:

Возвращаемое значение каждой итерации [].reduce, используемое как переменная a в следующей итерации.

Итерация 1: a = {x:1}, b = {x:2}, {x: 3}, назначенная a в Iteration 2

Итерация 2: a = {x:3}, b = {x:4}.

Проблема с вашим примером заключается в том, что вы возвращаете числовой литерал.

function (a, b) {
  return a.x + b.x; // returns number literal
}

Итерация 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

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

Ответ 2

Более чистый способ сделать это - предоставить начальное значение:

var arr = [{x:1}, {x:2}, {x:4}];
arr.reduce(function (acc, obj) { return acc + obj.x; }, 0); // 7
console.log(arr);

Ответ 3

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)

Давайте попробуем метод деструктуризации:

const arr = [ { x: 1 }, { x: 2 }, { x: 4 } ]
const result = arr.reduce( ( sum, { x } ) => sum + x , 0)
console.log( result ) // 7

Ответ 4

Другие ответили на этот вопрос, но я подумал, что буду подбрасывать другой подход. Вместо того, чтобы перейти непосредственно к суммированию a.x, вы можете объединить карту (от a.x до x) и уменьшить (добавить x):

arr = [{x:1},{x:2},{x:4}]
arr.map(function(a) {return a.x;})
   .reduce(function(a,b) {return a + b;});

По общему признанию, он, вероятно, будет немного медленнее, но я подумал, что стоит упомянуть его как вариант.

Ответ 5

Чтобы оформить то, что было указано, редуктор - это катаморфизм, который принимает два аргумента, которые могут быть одного и того же типа по совпадению, и возвращает тип, который соответствует первому аргументу.

function reducer (accumulator: X, currentValue: Y): X { }

Это означает, что тело редуктора должно быть связано с преобразованием currentValue и текущего значения accumulator в значение нового accumulator.

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

[1, 2, 3].reduce((x, y) => x + y);

Это просто работает, потому что это все числа.

[{ age: 5 }, { age: 2 }, { age: 8 }]
  .reduce((total, thing) => total + thing.age, 0);

Теперь мы даем начальное значение агрегатору. Начальное значение должно быть типом, который вы ожидаете от агрегатора (тип, который вы ожидаете в качестве конечного значения), в подавляющем большинстве случаев. Хотя вы не обязаны делать это (и не должны быть), важно помнить.

Как только вы это знаете, вы можете написать значимые сокращения для других проблем с отношением n: 1.

Удаление повторяющихся слов:

const skipIfAlreadyFound = (words, word) => words.includes(word)
    ? words
    : words.concat(word);

const deduplicatedWords = aBunchOfWords.reduce(skipIfAlreadyFound, []);

Обеспечение подсчета всех найденных слов:

const incrementWordCount = (counts, word) => {
  counts[word] = (counts[word] || 0) + 1;
  return counts;
};
const wordCounts = words.reduce(incrementWordCount, { });

Уменьшение массива массивов до одного плоского массива:

const concat = (a, b) => a.concat(b);

const numbers = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
].reduce(concat, []);

В любое время, когда вы хотите перейти от массива вещей к одному значению, которое не соответствует 1:1, сокращение - это то, что вы можете рассмотреть.

Фактически, карта и фильтр могут быть реализованы как сокращения:

const map = (transform, array) =>
  array.reduce((list, el) => list.concat(transform(el)), []);

const filter = (predicate, array) => array.reduce(
  (list, el) => predicate(el) ? list.concat(el) : list,
  []
);

Надеюсь, это даст дополнительный контекст использования reduce.

Единственное дополнение к этому, к которому я еще не вступил, - это когда ожидается, что типы ввода и вывода специально предназначены для динамического, потому что элементы массива являются функциями:

const compose = (...fns) => x =>
  fns.reduceRight((x, f) => f(x), x);

const hgfx = h(g(f(x)));
const hgf = compose(h, g, f);
const hgfy = hgf(y);
const hgfz = hgf(z);

Ответ 6

Для первой итерации 'a' будет первым объектом в массиве, следовательно, ax + bx вернет 1 + 2, т.е. 3. Теперь на следующей итерации возвращенные 3 присваиваются a, поэтому a является числом, теперь n вызывающим ax даст NaN.

Простое решение - сначала отобразить числа в массиве, а затем уменьшить их, как показано ниже:

arr.map(a=>a.x).reduce(function(a,b){return a+b})

здесь arr.map(a=>ax) предоставит массив чисел [1,2,4], теперь используя .reduce(function(a,b){return a+b}) просто добавит эти числа без каких-либо проблем

Другое простое решение - просто предоставить начальную сумму как ноль, присваивая 0 'a', как показано ниже:

arr.reduce(function(a,b){return a + b.x},0)

Ответ 7

На каждом шаге вашего сокращения вы не возвращаете новый объект {x:???}. Поэтому вам либо нужно делать:

arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a + b.x})

или вам нужно сделать

arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return {x: a.x + b.x}; }) 

Ответ 8

На первом этапе он будет работать нормально, так как значение a будет равно 1, а значение b будет равно 2, но при возврате 2 + 1 и на следующем шаге значение b будет возвращаемое значение from step 1 i.e 3, и поэтому b.x будет undefined... и undefined + anyNumber будет NaN, и именно поэтому вы получаете этот результат.

Вместо этого вы можете попробовать это, указав начальное значение как ноль i.e

arr.reduce(function(a,b){return a + b.x},0);

Ответ 9

функция сокращения выполняет итерацию по коллекции

arr = [{x:1},{x:2},{x:4}] // is a collection

arr.reduce(function(a,b){return a.x + b.x})

переводит на:

arr.reduce(
    //for each index in the collection, this callback function is called
  function (
    a, //a = accumulator ,during each callback , value of accumulator is 
         passed inside the variable "a"
    b, //currentValue , for ex currentValue is {x:1} in 1st callback
    currentIndex,
    array
  ) {
    return a.x + b.x; 
  },
  accumulator // this is returned at the end of arr.reduce call 
    //accumulator = returned value i.e return a.x + b.x  in each callback. 
);

во время каждого обратного вызова индекса, значение переменной "аккумулятора" передается в параметр "a" в функции обратного вызова. Если мы не инициализируем "аккумулятор", его значение будет undefined. Вызов undefined.x даст вам ошибку.

Чтобы решить эту проблему, инициализируйте "аккумулятор" со значением 0, как показал ответ Кейси.

Чтобы понять суть функции "уменьшить", я бы предложил вам посмотреть исходный код этой функции. Библиотека Lodash имеет функцию уменьшения, которая работает точно так же, как функция "уменьшить" в ES6.

Вот ссылка: уменьшить исходный код

Ответ 10

чтобы вернуть сумму всех реквизитов x:

arr.reduce(
(a,b) => (a.x || a) + b.x 
)

Ответ 11

Функция уменьшения массива принимает три параметра: initialValue (по умолчанию 0), аккумулятор и текущее значение. По умолчанию значение initialValue будет равно "0". который забирается аккумулятором

Давай увидим это в коде.

var arr =[1,2,4] ;
arr.reduce((acc,currVal) => acc + currVal ) ; 
// (remember Initialvalue is 0 by default )

//first iteration** : 0 +1 => Now accumulator =1;
//second iteration** : 1 +2 => Now accumulator =3;
//third iteration** : 3 + 4 => Now accumulator = 7;
No more array properties now the loop breaks .
// solution = 7

Теперь тот же пример с начальным значением:

var initialValue = 10;
var arr =[1,2,4] ;
arr.reduce((acc,currVal) => acc + currVal,initialValue ) ; 
/
// (remember Initialvalue is 0 by default but now it 10 )

//first iteration** : 10 +1 => Now accumulator =11;
//second iteration** : 11 +2 => Now accumulator =13;
//third iteration** : 13 + 4 => Now accumulator = 17;
No more array properties now the loop breaks .
//solution=17

То же самое относится и к массивам объектов (текущий вопрос stackoverflow):

var arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(acc,currVal){return acc + currVal.x}) 
// destructing {x:1} = currVal;
Now currVal is object which have all the object properties .So now 
currVal.x=>1 
//first iteration** : 0 +1 => Now accumulator =1;
//second iteration** : 1 +2 => Now accumulator =3;
//third iteration** : 3 + 4 => Now accumulator = 7;
No more array properties now the loop breaks 
//solution=7

Значение ONE IN ONE IN MINE по умолчанию равно 0, и ему можно дать все, что я имею в виду {}, [] и число

Ответ 12

    
  
 //fill creates array with n element
 //reduce requires 2 parameter , 3rd parameter as a length
 var fibonacci = (n) => Array(n).fill().reduce((a, b, c) => {
      return a.concat(c < 2 ? c : a[c - 1] + a[c - 2])
  }, [])
  console.log(fibonacci(8))

Ответ 13

вы не должны использовать топор для аккумулятора, вместо этого вы можете сделать это 'arr = [{x: 1}, {x: 2}, {x: 4}]

arr.reduce(function (a, b) {a + bx}, 0) '