Javascript Javascript减少对象数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5732043/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:37:25  来源:igfitidea点击:

Javascript reduce on array of objects

javascriptfunctional-programmingnode.jsreduce

提问by YXD

Say I want to sum a.xfor each element in arr.

假设我想对a.x中的每个元素求和arr

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

I have cause to believe that a.x is undefined at some point.

我有理由相信 ax 在某些时候是未定义的。

The following works fine

以下工作正常

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

What am I doing wrong in the first example?

我在第一个例子中做错了什么?

回答by Casey Chu

A cleaner way to accomplish this is by providing an initial value:

一种更简洁的方法是提供一个初始值:

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

The first time the anonymous function is called, it gets called with (0, {x: 1})and returns 0 + 1 = 1. The next time, it gets called with (1, {x: 2})and returns 1 + 2 = 3. It's then called with (3, {x: 4}), finally returning 7.

第一次调用匿名函数时,它被调用(0, {x: 1})并返回0 + 1 = 1。下一次,它被调用(1, {x: 2})并返回1 + 2 = 3。然后调用 with (3, {x: 4}),最后返回7

回答by JaredMcAteer

After the first iteration your're returning a number and then trying to get property xof it to add to the next object which is undefinedand maths involving undefinedresults in NaN.

经过第一次迭代your're返回一个数字,然后试图获得财产x的它添加到下一个对象,它是undefined和涉及数学undefined的结果NaN

try returning an object contain an xproperty with the sum of the x properties of the parameters:

尝试返回一个包含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}

Explanation added from comments:

从评论中添加的解释:

The return value of each iteration of [].reduceused as the avariable in the next iteration.

每次迭代的返回值[].reduce用作a下一次迭代的变量。

Iteration 1: a = {x:1}, b = {x:2}, {x: 3}assigned to ain Iteration 2

迭代 1: a = {x:1}, b = {x:2},在迭代 2 中{x: 3}分配给a

Iteration 2: a = {x:3}, b = {x:4}.

迭代 2: a = {x:3}, b = {x:4}.

The problem with your example is that you're returning a number literal.

您的示例的问题在于您返回的是数字文字。

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

Iteration 1: a = {x:1}, b = {x:2}, // returns 3as ain next iteration

迭代 1: a = {x:1}, b = {x:2},// returns 3a下一次迭代一样

Iteration 2: a = 3, b = {x:2}returns NaN

迭代 2: a = 3,b = {x:2}返回NaN

A number literal 3does not (typically) have a property called xso it's undefinedand undefined + b.xreturns NaNand NaN + <anything>is always NaN

数字文字3(通常)没有被调用的属性,x因此它是undefinedundefined + b.x返回NaN并且NaN + <anything>始终是NaN

Clarification: I prefer my method over the other top answer in this thread as I disagree with the idea that passing an optional parameter to reduce with a magic number to get out a number primitive is cleaner. It may result in fewer lines written but imo it is less readable.

澄清:我更喜欢我的方法而不是这个线程中的另一个最佳答案,因为我不同意传递可选参数以使用幻数减少以获取数字原语更清晰的想法。它可能会导致写入的行数减少,但 imo 可读性较差。

回答by Babakness

TL;DR, set the initial value

TL;DR,设置初始值

Using destructuring

使用解构

arr.reduce( ( sum, { x } ) => sum + x , 0)

arr.reduce( ( sum, { x } ) => sum + x , 0)

Without destructuring

无解构

arr.reduce( ( sum , cur ) => sum + cur.x , 0)

arr.reduce( ( sum , cur ) => sum + cur.x , 0)

With Typescript

使用打字稿

arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)

arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)

Let's try the destructuring method:

让我们试试解构方法:

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

The key to this is setting initial value. The return value becomes first parameter of the next iteration.

关键是设置初始值。返回值成为下一次迭代的第一个参数。

Technique used in top answer is not idiomatic

最佳答案中使用的技术不是惯用的

The accepted answer proposes NOT passing the "optional" value. This is wrong, as the idiomatic way is that the second parameter alwaysbe included. Why? Three reasons:

接受的答案建议不传递“可选”值。这是错误的,因为惯用的方式是始终包含第二个参数。为什么?三个原因:

1. Dangerous-- Not passing in the initial value is dangerous and can create side-effects and mutations if the callback function is careless.

1. 危险——不传入初始值是危险的,如果回调函数不小心会产生副作用和突变。

Behold

看哪

const badCallback = (a,i) => Object.assign(a,i) 

const foo = [ { a: 1 }, { b: 2 }, { c: 3 } ]
const bar = foo.reduce( badCallback )  // bad use of Object.assign
// Look, we've tampered with the original array
foo //  [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]

If however we had done it this way, with the initial value:

但是,如果我们这样做了,使用初始值:

const bar = foo.reduce( badCallback, {})
// foo is still OK
foo // { a: 1, b: 2, c: 3 }

For the record, unless you intend to mutate the original object, set the first parameter of Object.assignto an empty object. Like this: Object.assign({}, a, b, c).

为了记录,除非您打算改变原始对象,否则将 的第一个参数设置Object.assign为空对象。像这样:Object.assign({}, a, b, c)

2 - Better Type Inference--When using a tool like Typescript or an editor like VS Code, you get the benefit of telling the compiler the initial and it can catch errors if you're doing it wrong. If you don't set the initial value, in many situations it might not be able to guess and you could end up with creepy runtime errors.

2 - 更好的类型推断——当使用像 Typescript 这样的工具或像 VS Code 这样的编辑器时,你会得到告诉编译器初始值的好处,如果你做错了,它可以捕获错误。如果您不设置初始值,在许多情况下它可能无法猜测,最终可能会出现令人毛骨悚然的运行时错误。

3 - Respect the Functors-- JavaScript shines best when its inner functional child is unleashed. In the functional world, there is a standard on how you "fold" or reducean array. When you fold or apply a catamorphismto the array, you take the values of that array to construct a new type. You need to communicate the resulting type--you should do this even if the final type is that of the values in the array, another array, or any other type.

3 - 尊重函子——当 JavaScript 的内部函数子被释放时,它的表现最好。在函数式世界中,有一个关于如何“折叠”或reduce数组的标准。当您对数组折叠或应用catamorphism 时,您可以使用该数组的值来构造一个新类型。您需要传达结果类型——即使最终类型是数组、另一个数组或任何其他类型中的值,您也应该这样做。

Let's think about it another way. In JavaScript, functions can be pass around like data, this is how callbacks work, what is the result of the following code?

让我们换一种方式思考。在 JavaScript 中,函数可以像数据一样传递,这就是回调的工作原理,以下代码的结果是什么?

[1,2,3].reduce(callback)

[1,2,3].reduce(callback)

Will it return an number? An object? This makes it clearer

它会返回一个数字吗?一个东西?这样更清楚

[1,2,3].reduce(callback,0)

[1,2,3].reduce(callback,0)

Read more on the functional programming spec here: https://github.com/fantasyland/fantasy-land#foldable

在此处阅读有关函数式编程规范的更多信息:https: //github.com/fantasyland/fantasy-land#foldable

Some more background

更多背景

The reducemethod takes two parameters,

reduce方法有两个参数,

Array.prototype.reduce( callback, initialItem )

The callbackfunction takes the following parameters

callback函数采用以下参数

(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }

For the first iteration,

对于第一次迭代,

  • If initialItemis provided, the reducefunction passes the initialItemas the accumulatorand the first item of the array as the itemInArray.

  • If initialItemis notprovided, the reducefunction passes the first item in the array as the initialItemand the second item in the array as itemInArraywhich can be confusing behavior.

  • 如果initialItem提供,则该reduce函数将 传递initialItem为 ,accumulator并将数组的第一项传递为itemInArray

  • 如果initialItem提供时,reduce功能传递数组作为中的第一项initialItem和所述阵列中的第二项itemInArray可以被混淆的行为。

I teach and recommend always setting the initial value of reduce.

我教导并建议始终设置reduce的初始值。

You can check out the documentation at:

您可以在以下位置查看文档:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

Hope this helps!

希望这可以帮助!

回答by RHSeeger

Others have answered this question, but I thought I'd toss in another approach. Rather than go directly to summing a.x, you can combine a map (from a.x to x) and reduce (to add the x's):

其他人已经回答了这个问题,但我想我会采用另一种方法。您可以组合映射(从 ax 到 x)和减少(添加 x),而不是直接对 ax 求和:

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

Admittedly, it's probably going to be slightly slower, but I thought it worth mentioning it as an option.

诚然,它可能会稍微慢一点,但我认为值得一提的是它作为一种选择。

回答by Norguard

To formalize what has been pointed out, a reducer is a catamorphism which takes two arguments which may be the same type by coincidence, and returns a type which matches the first argument.

为了形式化已经指出的内容,reducer 是一种 catamorphism,它采用两个可能是相同类型的参数巧合,并返回与第一个参数匹配的类型。

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

That means that the body of the reducer needs to be about converting currentValueand the current value of the accumulatorto the value of the new accumulator.

这意味着 reducer 的主体需要将currentValue的当前值转换accumulator为 new 的值accumulator

This works in a straightforward way, when adding, because the accumulator and the element values both happen to be the same type (but serve different purposes).

这在添加时以一种直接的方式工作,因为累加器和元素值都恰好是相同的类型(但用于不同的目的)。

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

This just works because they're all numbers.

这只是有效,因为它们都是数字。

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

Now we're giving a starting value to the aggregator. The starting value should be the type that you expect the aggregator to be (the type you expect to come out as the final value), in the vast majority of cases. While you aren't forced to do this (and shouldn't be), it's important to keep in mind.

现在我们给聚合器一个起始值。在绝大多数情况下,起始值应该是您期望聚合器的类型(您期望作为最终值出现的类型)。虽然您不是被迫(也不应该)这样做,但记住这一点很重要。

Once you know that, you can write meaningful reductions for other n:1 relationship problems.

一旦你知道了这一点,你就可以为其他 n:1 关系问题写出有意义的归约。

Removing repeated words:

去除重复词:

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

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

Providing a count of all words found:

提供找到的所有单词的计数:

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

Reducing an array of arrays, to a single flat array:

将数组数组缩减为单个平面数组:

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

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

Any time you're looking to go from an array of things, to a single value that doesn't match a 1:1, reduce is something you might consider.

任何时候,当您希望从一组事物变为与 1:1 不匹配的单个值时,您都可能会考虑使用 reduce。

In fact, map and filter can both be implemented as reductions:

实际上,map 和 filter 都可以实现为reduction:

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,
  []
);

I hope this provides some further context for how to use reduce.

我希望这为如何使用reduce.

The one addition to this, which I haven't broken into yet, is when there is an expectation that the input and output types are specifically meant to be dynamic, because the array elements are functions:

除此之外,我还没有分解的一个补充是,当期望输入和输出类型专门是动态的时,因为数组元素是函数:

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);

回答by fatimasajjad

For the first iteration 'a' will be the first object in the array, hence a.x + b.x will return 1+2 i.e 3. Now in the next iteration the returned 3 is assigned to a, so a is a number now n calling a.x will give NaN.

对于第一次迭代,'a' 将是数组中的第一个对象,因此 ax + bx 将返回 1+2,即 3。现在在下一次迭代中,返回的 3 被分配给 a,所以 a 是一个数字,现在 n 调用 ax会给 NaN。

Simple solution is first mapping the numbers in array and then reducing them as below:

简单的解决方案是首先映射数组中的数字,然后按如下方式减少它们:

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

here arr.map(a=>a.x)will provide an array of numbers [1,2,4] now using .reduce(function(a,b){return a+b})will simple add these numbers without any hassel

这里arr.map(a=>a.x)将提供一组数字 [1,2,4] 现在使用.reduce(function(a,b){return a+b})将简单地添加这些数字而无需任何麻烦

Another simple solution is just to provide an initial sum as zero by assigning 0 to 'a' as below:

另一个简单的解决方案是通过将 0 分配给 'a' 来提供初始总和为零,如下所示:

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

回答by Jamie Wong

At each step of your reduce, you aren't returning a new {x:???}object. So you either need to do:

在 reduce 的每一步,你都不会返回一个新{x:???}对象。所以你要么需要做:

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

or you need to do

或者你需要做

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

回答by Nitesh Ranjan

In the first step, it will work fine as the value of awill be 1 and that of bwill be 2 but as 2+1 will be returned and in the next step the value of bwill be the return value from step 1 i.e 3and so b.xwill be undefined...and undefined + anyNumber will be NaN and that is why you are getting that result.

在第一步中,它会正常工作,因为 的值为a1,而的值为b2 但因为 2+1 将被返回,在下一步中,的值b将是返回值from step 1 i.e 3,因此b.x将是未定义的.. .and undefined + anyNumber 将是 NaN,这就是您得到该结果的原因。

Instead you can try this by giving initial value as zero i.e

相反,您可以通过将初始值设为零来尝试此操作,即

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


回答by Jo?o Ramires

If you have a complex object with a lot of data, like an array of objects, you can take a step by step approach to solve this.

如果您有一个包含大量数据的复杂对象,例如一个对象数组,您可以逐步解决这个问题。

For e.g:

例如:

const myArray = [{ id: 1, value: 10}, { id: 2, value: 20}];

First, you should map your array into a new array of your interest, it could be a new array of values in this example.

首先,您应该将数组映射到您感兴趣的新数组中,在本例中它可能是一个新的值数组。

const values = myArray.map(obj => obj.value);

This call back function will return a new array containing only values from the original array and store it on values const. Now your values const is an array like this:

此回调函数将返回一个仅包含原始数组中的值的新数组,并将其存储在值 const 上。现在你的值 const 是一个这样的数组:

values = [10, 20];

And now your are ready to perform your reduce:

现在你已经准备好执行你的reduce了:

const sum = values.reduce((accumulator, currentValue) => { return accumulator + currentValue; } , 0);

As you can see, the reduce method executes the call back function multiple times. For each time, it takes the current value of the item in the array and sum with the accumulator. So to properly sum it you need to set the initial value of your accumulator as the second argument of the reduce method.

如您所见,reduce 方法多次执行回调函数。每次,它都会获取数组中项目的当前值并与累加器相加。因此,要正确求和,您需要将累加器的初始值设置为 reduce 方法的第二个参数。

Now you have your new const sum with the value of 30.

现在您的新常量总和值为 30。

回答by SupineDread89

You could use the map and reduce functions

您可以使用 map 和 reduce 功能

const arr = [{x:1},{x:2},{x:4}];
const sum = arr.map(n => n.x).reduce((a, b) => a + b, 0);