假设我想对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
第一个例子中我做错了什么?
当前回答
如果你有一个包含大量数据的复杂对象,比如一个对象数组,你可以采取一步一步的方法来解决这个问题。
如:
const myArray = [{ id: 1, value: 10}, { id: 2, value: 20}];
首先,你应该将你的数组映射到一个你感兴趣的新数组,在这个例子中它可能是一个新的值数组。
const values = myArray.map(obj => obj.value);
这个回调函数将返回一个只包含原始数组中的值的新数组,并将其存储在values const中。现在你的values const是一个这样的数组:
values = [10, 20];
现在你已经准备好执行reduce了:
const sum = values.reduce((accumulator, currentValue) => { return accumulator + currentValue; } , 0);
如您所见,reduce方法多次执行回调函数。对于每一次,它都取数组中该项的当前值,并与累加器相加。为了正确地求和,你需要将累加器的初始值设置为reduce方法的第二个参数。
现在你有了新的const sum值为30。
其他回答
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
我们可以使用数组减少方法来创建新的对象,我们可以使用这个选项来求和或过滤
const FRUITS = ["apple", "orange"] const fruitBasket ={香蕉:{数量:10,kg:3},苹果:{数量:30,kg:10},橙子:{数量:1,kg:3}} const newFruitBasket =水果。Reduce ((acc, fruit) =>({…acc, [fruit]: fruitBasket[fruit]}), {}) console.log (newFruitBasket)
在第一步中,它会工作得很好,因为a的值将是1,b的值将是2,但由于2+1将返回,在下一步中,b的值将是第一步的返回值,即3,因此b.x将是未定义的…而undefined + anyNumber将是NaN,这就是为什么你会得到这个结果。
相反,你可以尝试给初始值为零,即
arr.reduce(function(a,b){return a + b.x},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;});
不可否认,它可能会稍微慢一些,但我认为值得把它作为一个选项提出来。