假设我想对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

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


当前回答

为了将所指出的内容形式化,reducer是一种变形,它接受两个可能碰巧是同一类型的参数,并返回与第一个参数匹配的类型。

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

这意味着减速器的主体需要将currentValue和累加器的当前值转换为新累加器的值。

这在添加时以一种简单的方式工作,因为累加器和元素值恰好是同一类型(但用途不同)。

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

其他回答

如果你有一个包含大量数据的复杂对象,比如一个对象数组,你可以采取一步一步的方法来解决这个问题。

如:

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。

let temp =[{x:1},
{x:2},
{x:3},
{x:4}];
let sum = temp.map(element => element.x).reduce((a, b) => a+ b , 0)
console.log(sum);

我们可以用这种方法求x的和

输出:10

在第一次迭代之后,你返回一个数字,然后试图获得它的属性x,以添加到下一个对象,这是未定义的,数学涉及未定义的结果在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}, b = {x:2}, {x: 3}分配给迭代2中的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}, //在下一次迭代中返回3作为a

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

数字文字3(通常)没有名为x的属性,因此它是未定义的,未定义+ b.x返回NaN, NaN + <任何>总是NaN

澄清:我更喜欢我的方法,而不是这个帖子中的其他答案,因为我不同意通过一个可选参数来减少一个神奇的数字来获得一个数字原语的想法。这可能会导致写的行数更少,但在我看来,可读性更差。

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

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

或者你需要这样做

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

//fill创建包含n元素的数组 //reduce需要2个参数,第3个参数作为长度 var fibonacci = (n) =>数组(n).fill()。Reduce ((a, b, c) => { 返回a.concat(c < 2 ?C: a[C - 1] + a[C - 2]) },[]) console.log(斐波纳契(8))