如何对对象数组调用reduce来求和它们的属性?
假设我想对 arr 中的每个元素求和 ax。
arr = [ { x: 1 }, { x: 2 }, { x: 4 } ];
arr.reduce(function(a, b){ return a.x + b.x; }); // => NaN
我有理由相信 ax
在某些时候是 undefined
。
以下工作正常
arr = [ 1, 2, 4 ];
arr.reduce(function(a, b){ return a + b; }); // => 7
我在第一个示例中做错了什么?
Say I want to sum a.x
for each element in 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.
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(23)
实现此目的的一种更简洁的方法是提供一个初始值作为
reduce
的第二个参数:第一次调用匿名函数时,会使用
(0, {x: 1})
调用它并返回0 + 1 = 1
。下一次,它会被(1, {x: 2})
调用并返回1 + 2 = 3
。然后使用(3, {x: 4})
调用它,最终返回7
。这也处理数组为空的情况,返回
0
。A cleaner way to accomplish this is by providing an initial value as the second argument to
reduce
:The first time the anonymous function is called, it gets called with
(0, {x: 1})
and returns0 + 1 = 1
. The next time, it gets called with(1, {x: 2})
and returns1 + 2 = 3
. It's then called with(3, {x: 4})
, finally returning7
.This also handles the case where the array is empty, returning
0
.第一次迭代后,您返回一个数字,然后尝试获取它的属性
x
以添加到下一个未定义
对象以及涉及未定义<的数学/code> 结果为
NaN
。尝试返回一个包含
x
属性的对象,其中包含参数的 x 属性的总和:从注释中添加的解释:
使用的
[].reduce
每次迭代的返回值作为下一次迭代中的a
变量。迭代 1:
a = {x:1}
、b = {x:2}
、{x: 3}
分配给迭代 2 中的 a
迭代 2:
a = {x:3}
、b = {x:4}
。您的示例的问题是您返回的是数字文字。
迭代 1:
a = {x:1}
、b = {x:2}
、// 返回 3
作为a< /code> 在下一次迭代
迭代 2 中:
a = 3
、b = {x:2}
返回NaN
数字文字
3
(通常)没有名为x
的属性,因此它是undefined
并且undefined + bx
返回NaN
> 和NaN +
始终为NaN
澄清:我更喜欢我的方法而不是这个线程中的其他最佳答案,因为我不同意传递可选参数以使用幻数进行归约以得到数字原语的想法更清晰。它可能会导致写入的行数减少,但在我看来它的可读性较差。
After the first iteration your're returning a number and then trying to get property
x
of it to add to the next object which isundefined
and maths involvingundefined
results inNaN
.try returning an object contain an
x
property with the sum of the x properties of the parameters:Explanation added from comments:
The return value of each iteration of
[].reduce
used as thea
variable in the next iteration.Iteration 1:
a = {x:1}
,b = {x:2}
,{x: 3}
assigned toa
in Iteration 2Iteration 2:
a = {x:3}
,b = {x:4}
.The problem with your example is that you're returning a number literal.
Iteration 1:
a = {x:1}
,b = {x:2}
,// returns 3
asa
in next iterationIteration 2:
a = 3
,b = {x:2}
returnsNaN
A number literal
3
does not (typically) have a property calledx
so it'sundefined
andundefined + b.x
returnsNaN
andNaN + <anything>
is alwaysNaN
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.
TL;DR,设置初始值
使用 解构
arr.reduce( ( sum, { x } ) => sum + x , 0)
不解构
arr.reduce( ( sum , cur ) => sum + cur.x , 0)
使用 TypeScript
arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)< /code>
让我们尝试一下解构方法:
其关键在于初始值的设定。返回值成为下一次迭代的第一个参数。
最佳答案中使用的技术不是惯用
的 接受的答案建议不传递“可选”值。这是错误的,因为惯用的方法是始终包含第二个参数。为什么?三个原因:
1。危险
-- 不传入初始值是危险的,如果回调函数不小心,可能会产生副作用和突变。
看吧:
但是,如果我们这样做,初始值是:
根据记录,除非您打算改变原始对象,否则请将
Object.assign
的第一个参数设置为空对象。像这样:Object.assign({}, a, b, c)
。2 - 更好的类型推断
--当使用像 TypeScript 这样的工具或像 VS Code 这样的编辑器时,您可以通过告诉编译器初始值来获得好处,并且如果您做错了,它可以捕获错误。如果您不设置初始值,在许多情况下它可能无法猜测,并且最终可能会出现令人毛骨悚然的运行时错误。
3 - 尊重函子
——当 JavaScript 的内部函数子被释放出来时,它就会发挥出最大的光芒。在函数世界中,有一个关于如何“折叠”或
减少
数组的标准。当您将 catamorphism 折叠或应用到数组时,您将使用该数组的值来构造一个新型。您需要传达结果类型 - 即使最终类型是数组、另一个数组或任何其他类型中的值,您也应该这样做。让我们换个角度思考一下。在 JavaScript 中,函数可以像数据一样传递,这就是回调的工作原理,以下代码的结果是什么?
[1,2,3].reduce(callback)
会返回一个数字吗?一个物体?这使得它更清晰
[1,2,3].reduce(callback,0)
在此处阅读有关函数式编程规范的更多信息:https://github.com/fantasyland/fantasy-land#foldable
更多背景信息
reduce
方法采用两个参数,回调
函数采用以下参数对于第一次迭代,
如果提供了
initialItem
,则reduce
函数将initialItem
作为累加器传递 并将数组的第一项作为itemInArray
。如果未提供
initialItem
,则reduce
函数将数组中的第一项作为initialItem
传递数组中的第二项为itemInArray
,这可能会造成混淆。我教导并建议始终设置reduce 的初始值。
您可以在以下位置查看文档:
https:// /developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
TL;DR, set the initial value
Using destructuring
arr.reduce( ( sum, { x } ) => sum + x , 0)
Without destructuring
arr.reduce( ( sum , cur ) => sum + cur.x , 0)
With TypeScript
arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)
Let's try the destructuring method:
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 always be 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.
Behold:
If however we had done it this way, with the initial value:
For the record, unless you intend to mutate the original object, set the first parameter of
Object.assign
to an empty object. Like this: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.
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
reduce
an array. When you fold or apply a catamorphism to 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.Let's think about it another way. In JavaScript, functions can be passed around like data, this is how callbacks work, what is the result of the following code?
[1,2,3].reduce(callback)
Will it return an number? An object? This makes it clearer
[1,2,3].reduce(callback,0)
Read more on the functional programming spec here: https://github.com/fantasyland/fantasy-land#foldable
Some more background
The
reduce
method takes two parameters,The
callback
function takes the following parametersFor the first iteration,
If
initialItem
is provided, thereduce
function passes theinitialItem
as theaccumulator
and the first item of the array as theitemInArray
.If
initialItem
is not provided, thereduce
function passes the first item in the array as theinitialItem
and the second item in the array asitemInArray
which can be confusing behavior.I teach and recommend always setting the initial value of reduce.
You can check out the documentation at:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
其他人已经回答了这个问题,但我想我应该采用另一种方法。您可以组合一个映射(从 ax 到 x)并减少(添加 x),而不是直接求和 ax:
诚然,它可能会稍微慢一些,但我认为值得一提的是它作为一个选项。
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):
Admittedly, it's probably going to be slightly slower, but I thought it worth mentioning it as an option.
为了形式化所指出的内容,reducer 是一种变形,它接受两个可能巧合为同一类型的参数,并返回与第一个参数匹配的类型。
这意味着减速器的主体需要将
currentValue
和accumulator
的当前值转换为新的accumulator
的值。在添加时,这是一种简单的方式,因为累加器和元素值恰好是相同的类型(但服务于不同的目的)。
这之所以有效,是因为它们都是数字。
现在我们为聚合器提供一个起始值。在绝大多数情况下,起始值应该是您期望聚合器的类型(您期望作为最终值出现的类型)。
虽然您没有被迫这样做(也不应该这样做),但请记住这一点很重要。
一旦了解了这一点,您就可以为其他 n:1 关系问题编写有意义的简化。
删除重复的单词:
提供找到的所有单词的计数:
将数组数组缩减为单个平面数组:
任何时候您想要从一组事物变为与 1 不匹配的单个值: 1、减少是你可以考虑的事情。
事实上,map 和filter 都可以实现为归约:
我希望这为如何使用
reduce
提供了一些进一步的背景信息。我还没有深入探讨的一个补充是,当期望输入和输出类型专门是动态的时,因为数组元素是函数:
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.
That means that the body of the reducer needs to be about converting
currentValue
and the current value of theaccumulator
to the value of the newaccumulator
.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).
This just works because they're all numbers.
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.
Removing repeated words:
Providing a count of all words found:
Reducing an array of arrays, to a single flat array:
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.
In fact, map and filter can both be implemented as reductions:
I hope this provides some further context for how to use
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:
对于第一次迭代,“a”将是数组中的第一个对象,因此 ax + bx 将返回 1+2,即 3。
现在,在下一次迭代中,返回的 3 被分配给 a,因此 a现在n是一个数字,调用
ax
将给出NaN
。简单的解决方案是首先映射数组中的数字,然后按如下方式减少它们:
这里
arr.map(a=>ax)
将提供一个数字数组 [1,2,4],现在使用 < code>.reduce(function(a,b){return a+b}) 将简单地将这些数字相加,没有任何麻烦另一个简单的解决方案是将 0 分配给 a 来提供初始总和为零如下:
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 giveNaN
.Simple solution is first mapping the numbers in array and then reducing them as below:
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 hasselAnother simple solution is just to provide an initial sum as zero by assigning 0 to
a
as below:如果您有一个包含大量数据的复杂对象(例如对象数组),您可以采取逐步方法来解决此问题。
例如:
首先,您应该将数组映射到您感兴趣的新数组中,在本例中它可能是一个新的值数组。
此回调函数将返回一个仅包含原始数组中的值的新数组,并将其存储在常量值中。现在你的values const 是一个像这样的数组:
现在你已经准备好执行reduce了:
正如你所看到的,reduce方法多次执行回调函数。每次,它都会获取数组中项目的当前值并与累加器求和。因此,要正确求和,您需要将累加器的初始值设置为 reduce 方法的第二个参数。
现在您有了新的 const sum,其值为 30。
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:
First, you should map your array into a new array of your interest, it could be a new array of values in this example.
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:
And now your are ready to perform your reduce:
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.
Now you have your new const sum with the value of 30.
在归约的每一步中,您都不会返回新的
{x:???}
对象。所以你要么需要做:要么你需要做
At each step of your reduce, you aren't returning a new
{x:???}
object. So you either need to do:or you need to do
我在 ES6 中做了一点改进:
返回数字
I did it in ES6 with a little improvement:
return number
在第一步中,它会正常工作,因为
a
的值为 1,b
的值为 2,但在下一步中将返回 2+1步骤b
的值将是步骤 1 的返回值,即 3,因此bx
将是未定义的...并且 undefined + anyNumber 将为 NaN,这就是你得到这个结果的原因。相反,您可以通过将初始值设置为零来尝试此操作,即
In the first step, it will work fine as the value of
a
will be 1 and that ofb
will be 2 but as 2+1 will be returned and in the next step the value ofb
will be the return valuefrom step 1 i.e 3
and sob.x
will be undefined...and undefined + anyNumber will be NaN and that is why you are getting that result.Instead you can try this by giving initial value as zero i.e
我曾经在我的开发中遇到过这种情况,我所做的是将我的解决方案包装在一个函数中,使其可以在我的环境中重用,如下所示:
I used to encounter this is my development, what I do is wrap my solution in a function to make it reusable in my environment, like this:
只是我的 2 美分关于使用对象文字设置默认值。
Just my 2 cents on setting a default value with object literal.
我们可以用这种方式求 x
输出的总和:10
we can used this way for sum of x
Output : 10
您可以使用reduce方法,如下所示;如果将 0(零)更改为 1 或其他数字,它会将其添加到总数中。例如,本示例给出的总数为 31,但是如果我们将 0 更改为 1,总数将为 32。
You can use reduce method as bellow; If you change the 0(zero) to 1 or other numbers, it will add it to total number. For example, this example gives the total number as 31 however if we change 0 to 1, total number will be 32.
刚刚根据之前给出的解决方案编写了一个通用函数。我是一名 Java 开发人员,因此对任何错误或非 javascript 标准表示歉意:-)
Just wrote a generic function from previously given solutions. I am a Java developer, so apologies for any mistakes or non-javascript standards :-)
通用打字稿函数:
示例:
A generic typescript function:
Example:
我们可以使用数组reduce方法来创建新对象,我们可以使用此选项来求和或过滤
We can use array reduce method to create new Object and we can use this option to sum or filter
reduce 函数迭代集合
转换为:
要了解“reduce”函数的输入输出,我建议您查看该函数的源代码。
Lodash库有reduce函数,其工作原理与ES6中的“reduce”函数完全相同。
这是链接:
减少源代码
reduce function iterates over a collection
translates to:
To understand the in-outs of "reduce" function, I would suggest you look at the source code of this function.
Lodash library has reduce function which works exactly same as "reduce" function in ES6.
Here is the link :
reduce source code
你不应该使用 ax 作为累加器,而是可以这样做
`arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){a + bx},0)`
you should not use a.x for accumulator , Instead you can do like this
`arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){a + b.x},0)`
返回所有
x
属性的总和:to return a sum of all
x
props:数组reduce函数需要三个参数,即initialValue(default
它是 0) 、累加器和当前值。
默认情况下,initialValue 的值为 "0" 。这是由
累加器
让我们在代码中看到这一点。
现在具有初始值的相同示例:
同样适用于对象数组(当前的 stackoverflow 问题):
要记住的一件事是 InitialValue 默认为 0,并且可以给出任何我想要的值:{}、[] 和数字
Array reduce function takes three parameters i.e, initialValue(default
it's 0) , accumulator and current value .
By default the value of initialValue will be "0" . which is taken by
accumulator
Let's see this in code .
Now same example with initial Value :
Same applies for the object arrays as well(the current stackoverflow question) :
ONE THING TO BARE IN MIND is InitialValue by default is 0 and can be given anything i mean {},[] and number