假设我想对arr中的每个元素求和。
arr = [ { x: 1 }, { x: 2 }, { x: 4 } ];
arr.reduce(function(a, b){ return a.x + b.x; }); // => NaN
我有理由相信a。x在某些时候是没有定义的。
以下工作正常
arr = [ 1, 2, 4 ];
arr.reduce(function(a, b){ return a + b; }); // => 7
第一个例子中我做错了什么?
假设我想对arr中的每个元素求和。
arr = [ { x: 1 }, { x: 2 }, { x: 4 } ];
arr.reduce(function(a, b){ return a.x + b.x; }); // => NaN
我有理由相信a。x在某些时候是没有定义的。
以下工作正常
arr = [ 1, 2, 4 ];
arr.reduce(function(a, b){ return a + b; }); // => 7
第一个例子中我做错了什么?
当前回答
一个更简洁的方法是提供一个初始值作为reduce的第二个参数:
Var arr = [{x:1}, {x:2}, {x:4}]; Var结果= arr。Reduce(函数(acc, obj){返回acc + obj.x;}, 0); console.log(结果);/ / 7
第一次调用匿名函数时,使用(0,{x: 1})调用它,并返回0 + 1 = 1。下一次,它被(1,{x: 2})调用,并返回1 + 2 = 3。然后用(3,{x: 4})调用它,最后返回7。
这也可以处理数组为空的情况,返回0。
其他回答
返回所有x个道具的和:
arr.reduce(
(a,b) => (a.x || a) + b.x
)
TL;DR,设置初始值
使用解构
加勒比海盗。Reduce ((sum, {x}) => sum + x, 0)
没有解构
加勒比海盗。Reduce ((sum, cur) => sum + cur.x, 0)
与打印稿
加勒比海盗。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) //
关键在于设置初始值。返回值将成为下一次迭代的第一个参数。
上面回答的技巧不是惯用的
接受的答案建议不传递“可选”值。这是错误的,因为惯用的方法是总是包括第二个参数。为什么?三个原因:
1. 危险的 不传递初始值是危险的,如果回调函数不小心,可能会产生副作用和突变。
看哪
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 } ]
如果我们这样做,用初始值:
const bar = foo.reduce( badCallback, {})
// foo is still OK
foo // { a: 1, b: 2, c: 3 }
为了记录,除非您打算改变原始对象,否则请设置object的第一个参数。赋值给空对象。像这样:对象。赋值({},a, b, c)
2 -更好的类型推断 当你使用Typescript这样的工具或VS Code这样的编辑器时,你可以告诉编译器初始值,如果你做错了,它可以捕捉到错误。如果您不设置初始值,在许多情况下,它可能无法猜测,最终可能会出现令人毛骨悚然的运行时错误。
3 -尊重函子 当JavaScript内部的子函数被释放出来时,它的表现最好。在函数领域,有一个关于如何“折叠”或缩小数组的标准。当对数组折叠或应用变换时,将获取该数组的值来构造一个新类型。您需要传达结果类型——即使最终类型是数组、另一个数组或任何其他类型中的值的类型,您也应该这样做。
我们换个角度考虑。在JavaScript中,函数可以像数据一样传递,这就是回调的工作方式,下面的代码的结果是什么?
(1、2、3).reduce(回调)
它会返回一个数字吗?一个对象?这样更清楚
[1,2,3]。减少(回调,0)
在这里阅读更多函数式编程规范:https://github.com/fantasyland/fantasy-land#foldable
更多背景信息
reduce方法有两个参数,
Array.prototype.reduce( callback, initialItem )
回调函数接受以下参数
(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }
在第一次迭代中,
如果提供了initialItem,则reduce函数将initialItem作为累加器传递,并将数组的第一项作为itemInArray传递。 如果没有提供initialItem,则reduce函数将数组中的第一项传递为initialItem,将数组中的第二项传递为itemInArray,这可能会造成混淆。
我教授并建议设置reduce的初始值。
您可以在以下地址查看文档:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
一个更简洁的方法是提供一个初始值作为reduce的第二个参数:
Var arr = [{x:1}, {x:2}, {x:4}]; Var结果= arr。Reduce(函数(acc, obj){返回acc + obj.x;}, 0); console.log(结果);/ / 7
第一次调用匿名函数时,使用(0,{x: 1})调用它,并返回0 + 1 = 1。下一次,它被(1,{x: 2})调用,并返回1 + 2 = 3。然后用(3,{x: 4})调用它,最后返回7。
这也可以处理数组为空的情况,返回0。
你不应该使用a.x作为累加器,相反,你可以这样做 ' arr = [{x:1},{x:2},{x:4}]
Arr.reduce(函数(a,b){a + b.x},0) '
其他人已经回答了这个问题,但我认为我应该提出另一种方法。你可以结合一个映射(从a.x到x)和reduce(把x相加),而不是直接对a.x求和:
arr = [{x:1},{x:2},{x:4}]
arr.map(function(a) {return a.x;})
.reduce(function(a,b) {return a + b;});
不可否认,它可能会稍微慢一些,但我认为值得把它作为一个选项提出来。