Javascript减少了对象数组。

时间:2022-02-27 22:04:26

Say I want to sum a.x for each element in arr.

假设我想求和。在arr中每个元素的x。

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.

我有理由相信a。x在某个点没有定义。

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?

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

8 个解决方案

#1


167  

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 is undefined and maths involving undefined results in NaN.

在第一次迭代之后,您返回一个数字,然后试图获得它的属性x,以添加到下一个未定义的对象,并且在NaN中包含未定义的结果。

try returning an object contain an x property with the sum of the x properties of the parameters:

尝试返回一个对象包含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}

Explanation added from comments:

从评论解释说:

The return value of each iteration of [].reduce used as the a variable in the next iteration.

每个迭代的返回值[]。在下一次迭代中减少用作变量。

Iteration 1: a = {x:1}, b = {x:2}, {x: 3} assigned to a in Iteration 2

迭代1:a = {x:1}, b = {x:2}, {x: 3}在迭代2中分配给a。

Iteration 2: a = {x:3}, b = {x:4}.

迭代2:a = {x:3}, b = {x:4}。

The problem with your example is that you're returning a number literal.

你的例子的问题是你返回的是数字文字。

function (a, b) {
  return a.x + b.x; // returns number literal
}

Iteration 1: a = {x:1}, b = {x:2}, // returns 3 as a in next iteration

迭代1:a = {x:1}, b = {x:2}, //在下一次迭代中返回3。

Iteration 2: a = 3, b = {x:2} returns NaN

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

A number literal 3 does not (typically) have a property called x so it's undefined and undefined + b.x returns NaN and NaN + <anything> is always NaN

数字3不(通常)有一个名为x的属性,所以它是未定义的和未定义的+ b。返回NaN和NaN + <任何> 都是NaN。

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.

澄清:我更喜欢我的方法,而不是在这个线程的另一个顶部的答案,因为我不同意通过一个可选参数来减少一个数字,以得到一个数字原语更简洁的想法。它可能导致写的行数减少,但imo的可读性更差。

#2


165  

A cleaner way to accomplish this is by providing an initial value:

实现这一目标的更简洁的方法是提供一个初始值:

var arr = [{x:1}, {x:2}, {x:4}];
arr.reduce(function (acc, obj) { return acc + obj.x; }, 0); // 7

The first time the anonymous function is called, it gets called with (0, {x: 1}) and returns 0 + 1 = 1. The next time, it gets called with (1, {x: 2}) and returns 1 + 2 = 3. It's then called with (3, {x: 4}), finally returning 7.

第一次调用匿名函数时,它会调用(0,{x: 1})并返回0 + 1 = 1。下一次,它将被调用(1,{x: 2}),返回1 + 2 = 3。然后调用它(3,{x: 4}),最后返回7。

#3


19  

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):

其他人回答了这个问题,但我想我还是换个方法吧。而不是直接去求和。x,你可以把地图(从a)合并到一起。x到x)并减少(添加x):

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

Admittedly, it's probably going to be slightly slower, but I thought it worth mentioning it as an option.

诚然,它可能会稍微慢一些,但我认为它值得一提。

#4


14  

TL;DR, set the initial value

TL;DR,设置初始值。

arr.reduce( (sum,cur) => sum+cur.x , 0)

加勒比海盗。减少((和,cur)= >总和+坏蛋。x,0)

The key to this is setting initial value. The return value becomes first parameter of the next iteration.

关键是设置初始值。返回值成为下一个迭代的第一个参数。

First pass, sum is zero. cur is the first value in the array, an object. So we access the x property. We add that number to sum and return it.

第一遍,和是零。cur是数组中的第一个值,一个对象。我们访问x属性。我们将这个数字加起来并返回。

Next iteration, same thing. sum is now the last returned value and we add the next item and so on.

下一个迭代中,同样的事情。sum现在是最后一个返回值,我们添加下一个项等等。

If we didn't set that 0 at the end, the first iteration sum would be an object and we'd write logic to handle an object. We'd also have to return an object because the next iteration is setup to handle an object.

如果我们在最后没有设置0,那么第一个迭代和将是一个对象,我们将编写逻辑来处理一个对象。我们还必须返回一个对象,因为下一个迭代是用来处理一个对象的。

A little more background

更多的背景

The reduce method takes two parameters,

reduce方法采用两个参数,

Array.prototype.reduce( callback, initialItem )

The callback function takes the following parameters

回调函数接受以下参数。

(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }

When initialItem is provided, it is passed in as accumulator into the callback function on first past and itemInArray is the first item in the array.

当提供initialItem时,它将作为累加器传入回调函数,而itemInArray是数组中的第一项。

If initialItem isn't set, the reduce takes the first item in the array as initialItem and passes the the second item in as itemInArray. This can be confusing behavior.

如果未设置initialItem,则reduce将数组中的第一项作为initialItem,并将第二个项目作为itemInArray传递。这可能是令人困惑的行为。

I teach and recommend always setting the initial value of reduce. Furthermore, as you grow as a developer and start reaching for transducers or other enhanced libraries, you'll find that setting the initial value is a must.

我教授和建议总是设定减少的初始值。此外,随着您作为开发人员的成长,并开始接触传感器或其他增强的库,您会发现设置初始值是必须的。

For a full article on Array.prototype.reduce see:

关于Array.prototype的完整文章。减少看:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

Hope this helps!

希望这可以帮助!

#5


6  

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.

为了使所指出的内容形式化,减少器是一个catamorphism,它接受两个参数,它们可能是同一类型的巧合,并返回一个匹配第一个参数的类型。

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

That means that the body of the reducer needs to be about converting currentValue and the current value of the accumulator to the value of the new accumulator.

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

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).

当添加时,这是一种简单的方法,因为累加器和元素值都是相同的类型(但服务于不同的目的)。

[1, 2, 3].reduce((x, y) => x + y);

This just works because they're all numbers.

这行得通,因为它们都是数字。

[{ age: 5 }, { age: 2 }, { age: 8 }]
  .reduce((total, thing) => total + thing.age, 0);

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.

一旦你知道了这一点,你就可以为其他n:1关系问题写下有意义的削减。

Removing repeated words:

删除重复的词:

const skipIfAlreadyFound = (words, word) => words.includes(word)
    ? words
    : words.concat(word);

const deduplicatedWords = aBunchOfWords.reduce(skipIfAlreadyFound, []);

Providing a count of all words found:

提供所有发现的字数:

const incrementWordCount = (counts, word) => {
  counts[word] = (counts[word] || 0) + 1;
  return counts;
};
const wordCounts = words.reduce(incrementWordCount, { });

Reducing an array of arrays, to a single flat array:

减少数组的数组,到一个单一的平面阵列:

const concat = (a, b) => a.concat(b);

const numbers = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
].reduce(concat, []);

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.

任何时候,当你想要从一大堆东西,到一个不匹配1:1的值时,你可能会考虑减少。

In fact, map and filter can both be implemented as reductions:

实际上,map和filter都可以实现为reduce:

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

I hope this provides some further context for how to use reduce.

我希望这为如何使用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:

除此之外,我还没有讲到,当期望输入和输出类型是动态的,因为数组元素是函数:

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

#6


4  

At each step of your reduce, you aren't returning a new {x:???} object. So you either need to do:

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

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

or you need to do

或者你需要做。

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

#7


0  

reduce function iterates over a collection

reduce函数在集合上迭代。

arr = [{x:1},{x:2},{x:4}] // is a collection

arr.reduce(function(a,b){return a.x + b.x})

translates to:

翻译:

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

during each index callback, value of variable "accumulator" is passed into "a" parameter in the callback function. If we don't initialize "accumulator", its value will be undefined. Calling undefined.x would give you error.

在每个索引回调过程中,变量“累加器”的值被传递到回调函数中的“一个”参数中。如果我们不初始化“累加器”,它的值将是未定义的。调用定义。x会给出误差。

To solve this, initialize "accumulator" with value 0 as Casey's answer showed above.

为了解决这个问题,初始化“累加器”,值为0,正如Casey的回答所示。

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.

要理解“reduce”函数的输出,我建议您查看该函数的源代码。在ES6中,Lodash库的功能与“reduce”功能完全相同。

Here is the link : reduce source code

下面是链接:减少源代码。

#8


0  

In the first step, it will work fine as the value of a will be 1 and that of b will be 2 but as 2+1 will be returned and in the next step the value of b will be the return value from step 1 i.e 3 and so b.x will be undefined...and undefined + anyNumber will be NaN and that is why you are getting that result.

在第一步中,a的值是1,而b的值是2,但当2+1返回时,b的值将是第1步的返回值。e 3和b。x将未定义……undefined + anyNumber是NaN,这就是为什么你得到这个结果。

Instead you can try this by giving initial value as zero i.e

arr.reduce(function(a,b){return a + b.x},0);

#1


167  

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 is undefined and maths involving undefined results in NaN.

在第一次迭代之后,您返回一个数字,然后试图获得它的属性x,以添加到下一个未定义的对象,并且在NaN中包含未定义的结果。

try returning an object contain an x property with the sum of the x properties of the parameters:

尝试返回一个对象包含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}

Explanation added from comments:

从评论解释说:

The return value of each iteration of [].reduce used as the a variable in the next iteration.

每个迭代的返回值[]。在下一次迭代中减少用作变量。

Iteration 1: a = {x:1}, b = {x:2}, {x: 3} assigned to a in Iteration 2

迭代1:a = {x:1}, b = {x:2}, {x: 3}在迭代2中分配给a。

Iteration 2: a = {x:3}, b = {x:4}.

迭代2:a = {x:3}, b = {x:4}。

The problem with your example is that you're returning a number literal.

你的例子的问题是你返回的是数字文字。

function (a, b) {
  return a.x + b.x; // returns number literal
}

Iteration 1: a = {x:1}, b = {x:2}, // returns 3 as a in next iteration

迭代1:a = {x:1}, b = {x:2}, //在下一次迭代中返回3。

Iteration 2: a = 3, b = {x:2} returns NaN

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

A number literal 3 does not (typically) have a property called x so it's undefined and undefined + b.x returns NaN and NaN + <anything> is always NaN

数字3不(通常)有一个名为x的属性,所以它是未定义的和未定义的+ b。返回NaN和NaN + <任何> 都是NaN。

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.

澄清:我更喜欢我的方法,而不是在这个线程的另一个顶部的答案,因为我不同意通过一个可选参数来减少一个数字,以得到一个数字原语更简洁的想法。它可能导致写的行数减少,但imo的可读性更差。

#2


165  

A cleaner way to accomplish this is by providing an initial value:

实现这一目标的更简洁的方法是提供一个初始值:

var arr = [{x:1}, {x:2}, {x:4}];
arr.reduce(function (acc, obj) { return acc + obj.x; }, 0); // 7

The first time the anonymous function is called, it gets called with (0, {x: 1}) and returns 0 + 1 = 1. The next time, it gets called with (1, {x: 2}) and returns 1 + 2 = 3. It's then called with (3, {x: 4}), finally returning 7.

第一次调用匿名函数时,它会调用(0,{x: 1})并返回0 + 1 = 1。下一次,它将被调用(1,{x: 2}),返回1 + 2 = 3。然后调用它(3,{x: 4}),最后返回7。

#3


19  

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):

其他人回答了这个问题,但我想我还是换个方法吧。而不是直接去求和。x,你可以把地图(从a)合并到一起。x到x)并减少(添加x):

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

Admittedly, it's probably going to be slightly slower, but I thought it worth mentioning it as an option.

诚然,它可能会稍微慢一些,但我认为它值得一提。

#4


14  

TL;DR, set the initial value

TL;DR,设置初始值。

arr.reduce( (sum,cur) => sum+cur.x , 0)

加勒比海盗。减少((和,cur)= >总和+坏蛋。x,0)

The key to this is setting initial value. The return value becomes first parameter of the next iteration.

关键是设置初始值。返回值成为下一个迭代的第一个参数。

First pass, sum is zero. cur is the first value in the array, an object. So we access the x property. We add that number to sum and return it.

第一遍,和是零。cur是数组中的第一个值,一个对象。我们访问x属性。我们将这个数字加起来并返回。

Next iteration, same thing. sum is now the last returned value and we add the next item and so on.

下一个迭代中,同样的事情。sum现在是最后一个返回值,我们添加下一个项等等。

If we didn't set that 0 at the end, the first iteration sum would be an object and we'd write logic to handle an object. We'd also have to return an object because the next iteration is setup to handle an object.

如果我们在最后没有设置0,那么第一个迭代和将是一个对象,我们将编写逻辑来处理一个对象。我们还必须返回一个对象,因为下一个迭代是用来处理一个对象的。

A little more background

更多的背景

The reduce method takes two parameters,

reduce方法采用两个参数,

Array.prototype.reduce( callback, initialItem )

The callback function takes the following parameters

回调函数接受以下参数。

(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }

When initialItem is provided, it is passed in as accumulator into the callback function on first past and itemInArray is the first item in the array.

当提供initialItem时,它将作为累加器传入回调函数,而itemInArray是数组中的第一项。

If initialItem isn't set, the reduce takes the first item in the array as initialItem and passes the the second item in as itemInArray. This can be confusing behavior.

如果未设置initialItem,则reduce将数组中的第一项作为initialItem,并将第二个项目作为itemInArray传递。这可能是令人困惑的行为。

I teach and recommend always setting the initial value of reduce. Furthermore, as you grow as a developer and start reaching for transducers or other enhanced libraries, you'll find that setting the initial value is a must.

我教授和建议总是设定减少的初始值。此外,随着您作为开发人员的成长,并开始接触传感器或其他增强的库,您会发现设置初始值是必须的。

For a full article on Array.prototype.reduce see:

关于Array.prototype的完整文章。减少看:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

Hope this helps!

希望这可以帮助!

#5


6  

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.

为了使所指出的内容形式化,减少器是一个catamorphism,它接受两个参数,它们可能是同一类型的巧合,并返回一个匹配第一个参数的类型。

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

That means that the body of the reducer needs to be about converting currentValue and the current value of the accumulator to the value of the new accumulator.

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

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).

当添加时,这是一种简单的方法,因为累加器和元素值都是相同的类型(但服务于不同的目的)。

[1, 2, 3].reduce((x, y) => x + y);

This just works because they're all numbers.

这行得通,因为它们都是数字。

[{ age: 5 }, { age: 2 }, { age: 8 }]
  .reduce((total, thing) => total + thing.age, 0);

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.

一旦你知道了这一点,你就可以为其他n:1关系问题写下有意义的削减。

Removing repeated words:

删除重复的词:

const skipIfAlreadyFound = (words, word) => words.includes(word)
    ? words
    : words.concat(word);

const deduplicatedWords = aBunchOfWords.reduce(skipIfAlreadyFound, []);

Providing a count of all words found:

提供所有发现的字数:

const incrementWordCount = (counts, word) => {
  counts[word] = (counts[word] || 0) + 1;
  return counts;
};
const wordCounts = words.reduce(incrementWordCount, { });

Reducing an array of arrays, to a single flat array:

减少数组的数组,到一个单一的平面阵列:

const concat = (a, b) => a.concat(b);

const numbers = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
].reduce(concat, []);

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.

任何时候,当你想要从一大堆东西,到一个不匹配1:1的值时,你可能会考虑减少。

In fact, map and filter can both be implemented as reductions:

实际上,map和filter都可以实现为reduce:

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

I hope this provides some further context for how to use reduce.

我希望这为如何使用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:

除此之外,我还没有讲到,当期望输入和输出类型是动态的,因为数组元素是函数:

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

#6


4  

At each step of your reduce, you aren't returning a new {x:???} object. So you either need to do:

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

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

or you need to do

或者你需要做。

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

#7


0  

reduce function iterates over a collection

reduce函数在集合上迭代。

arr = [{x:1},{x:2},{x:4}] // is a collection

arr.reduce(function(a,b){return a.x + b.x})

translates to:

翻译:

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

during each index callback, value of variable "accumulator" is passed into "a" parameter in the callback function. If we don't initialize "accumulator", its value will be undefined. Calling undefined.x would give you error.

在每个索引回调过程中,变量“累加器”的值被传递到回调函数中的“一个”参数中。如果我们不初始化“累加器”,它的值将是未定义的。调用定义。x会给出误差。

To solve this, initialize "accumulator" with value 0 as Casey's answer showed above.

为了解决这个问题,初始化“累加器”,值为0,正如Casey的回答所示。

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.

要理解“reduce”函数的输出,我建议您查看该函数的源代码。在ES6中,Lodash库的功能与“reduce”功能完全相同。

Here is the link : reduce source code

下面是链接:减少源代码。

#8


0  

In the first step, it will work fine as the value of a will be 1 and that of b will be 2 but as 2+1 will be returned and in the next step the value of b will be the return value from step 1 i.e 3 and so b.x will be undefined...and undefined + anyNumber will be NaN and that is why you are getting that result.

在第一步中,a的值是1,而b的值是2,但当2+1返回时,b的值将是第1步的返回值。e 3和b。x将未定义……undefined + anyNumber是NaN,这就是为什么你得到这个结果。

Instead you can try this by giving initial value as zero i.e

arr.reduce(function(a,b){return a + b.x},0);