说我想对a.x
中的每个元素求和arr
。
arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN
我有理由相信斧头在某些时候是不确定的。
以下工作正常
arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7
我在第一个示例中做错了什么?
说我想对a.x
中的每个元素求和arr
。
arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN
我有理由相信斧头在某些时候是不确定的。
以下工作正常
arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7
我在第一个示例中做错了什么?
Answers:
第一次迭代后,您将返回一个数字,然后尝试获取该数字的属性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}
分配给在迭代2b = {x:2}
{x: 3}
a
迭代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
澄清:我更喜欢我的方法,而不是该线程中的其他最高答案,因为我不同意这样的想法,即传递一个可选参数以使用幻数来减少以得出数字基元会更干净。这可能导致写入的行数减少,但imo的可读性降低。
一种更清洁的方法是通过提供一个初始值:
var arr = [{x:1}, {x:2}, {x:4}];
arr.reduce(function (acc, obj) { return acc + obj.x; }, 0); // 7
console.log(arr);
首次调用匿名函数时,将使用调用(0, {x: 1})
并返回0 + 1 = 1
。下次,它被调用(1, {x: 2})
并返回1 + 2 = 3
。然后调用(3, {x: 4})
,最后返回7
。
reduce
。
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
关键是设置初始值。返回值成为下一次迭代的第一个参数。
接受的答案建议不通过“可选”值。这是错误的,因为惯用的方法是始终包含第二个参数。为什么?三个原因:
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.assign
为空对象。像这样:Object.assign({}, a, b, c)
。
2-更好的类型推断 -使用诸如Typescript之类的工具或诸如VS Code之类的编辑器时,您将受益于告诉编译器的初始信息,并且如果做错了它可以捕获错误。如果您未设置初始值,则在许多情况下它可能无法猜测,最终可能会出现令人毛骨悚然的运行时错误。
3-尊重
函子-当释放内部功能子级的JavaScript时,它的发光效果最佳。在功能世界中,有一个关于如何“折叠”或reduce
排列数组的标准。当您对数组折叠或应用变形时,您将使用该数组的值来构造新的类型。您需要传达结果类型-即使最终类型是数组,另一个数组或任何其他类型中的值的类型,也应该这样做。
让我们换一种方式思考。在JavaScript中,函数可以像数据一样传递,这就是回调的工作方式,以下代码的结果是什么?
[1,2,3].reduce(callback)
它会返回数字吗?一个东西?这使它更清晰
[1,2,3].reduce(callback,0)
在此处阅读有关函数式编程规范的更多信息:https : //github.com/fantasyland/fantasy-land#foldable
该reduce
方法有两个参数,
Array.prototype.reduce( callback, initialItem )
该callback
函数采用以下参数
(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }
对于第一次迭代,
如果initialItem
提供,则reduce
函数将传递initialItem
为accumulator
,将数组的第一项传递为itemInArray
。
如果initialItem
是不提供时,reduce
功能传递数组作为中的第一项initialItem
和所述阵列中的第二项itemInArray
可以被混淆的行为。
我教导并建议始终设置reduce的初始值。
您可以在以下位置查看文档:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
希望这可以帮助!
为了使所指出的内容形式化,reducer是一种变形,它采用巧合的方式接受两个可能是相同类型的参数,然后返回与第一个参数匹配的类型。
function reducer (accumulator: X, currentValue: Y): X { }
这意味着减速器的主体需要将转换currentValue
并将的当前值转换accumulator
为new的值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不匹配的单个值时,都可以考虑使用reduce方法。
实际上,map和filter都可以实现为简化:
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);
对于第一次迭代,“ 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=>a.x)
将提供数字[1,2,4]的数组,现在使用.reduce(function(a,b){return a+b})
将简单地添加这些数字而不会产生任何麻烦
另一个简单的解决方案是通过将0分配给'a'来提供初始和为零:
arr.reduce(function(a,b){return a + b.x},0)
第一步,它将正常工作,因为will的值a
将为1,而b
will 的值为2,但是由于将返回2 + 1,而在下一步中,of的值b
将是返回值from step 1 i.e 3
,因此b.x
将是不确定的。 .and undefined + anyNumber将为NaN,这就是为什么要得到该结果的原因。
arr.reduce(function(a,b){return a + b.x},0);
如果您有一个包含大量数据的复杂对象(如对象数组),则可以采用逐步的方法来解决此问题。
例如:
const myArray = [{ id: 1, value: 10}, { id: 2, value: 20}];
首先,您应该将数组映射到您感兴趣的新数组,在此示例中,它可能是新的值数组。
const values = myArray.map(obj => obj.value);
此回调函数将返回一个仅包含原始数组中值的新数组,并将其存储在值const上。现在,您的值const是一个像这样的数组:
values = [10, 20];
现在,您可以执行还原了:
const sum = values.reduce((accumulator, currentValue) => { return accumulator + currentValue; } , 0);
如您所见,reduce方法多次执行回调函数。对于每次,它都会获取数组中该项的当前值,并与累加器求和。因此,要正确求和,您需要将累加器的初始值设置为reduce方法的第二个参数。
现在您有了新的const和,其值为30。
减少功能迭代集合
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.x会给您错误。
要解决此问题,请使用上面显示的Casey答案将值0初始化为“累加器” 。
为了了解“ reduce”功能的内在,我建议您看一下该功能的源代码。Lodash库具有reduce函数,其功能与ES6中的reduce函数完全相同。
这里是链接: 减少源代码
数组归约函数采用三个参数,即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's 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
要注意的一件事情是,默认情况下,InitialValue为0,并且可以赋予我任何意思,{},[]和数字
//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))
arr.reduce(function(a,b){return a + b})
是第二个例子。