[英]Use ES6 reduce or map to compute sums and percentages in nested objects of an Array
如果标题不是特别清楚,请道歉。 我在下面简要介绍了我正在尝试做的事情:
var arrayOfPeopleStats = [
person1 = {
this: { makes: 10, attempts: 15, pct: .666667 },
that: { makes: 12, attempts: 16, pct: .75 },
thos: { value: 10, rank: 24 },
them: { value: 15, rank: 11 }
},
person2 = {
this: { makes: 5, attempts: 15, pct: .333333 },
that: { makes: 10, attempts: 12, pct: .83333 },
thos: { value: 4, rank: 40 },
them: { value: 3, rank: 32 }
},
person3 = {
this: { makes: 14, attempts: 14, pct: 1 },
that: { makes: 7, attempts: 8, pct: .875 },
thos: { value: 12, rank: 12 },
them: { value: 13, rank: 10 }
}
]
var desiredOutput = [
allPeople: {
this: { makes: 29, attempts: 44, pct: .65909 },
that: { makes: 29, attempts: 36, pct: .80555 },
thos: { value: 26, rank: doesnt matter },
them: { value: 31, rank: doesnt matter }
}
]
arrayOfPeopleStats
是一个包含person对象的数组。 每个人对象都有一些统计数据(在这个例子中,这个,他们,在这个例子中),并且这些统计数据中的每一个都是具有三个makes, attempts, steals
或value, rank
。
我想对每个人的stat对象求和,尝试和值,并计算总和数据的新pct
值。
我将很快发布我使用ES6 reduce完成的尝试,虽然我不确定这是解决问题的最佳方法。 任何帮助都非常感谢!
它将帮助您开始考虑类型方面的数据。
一个Person
是一个有四个属性的对象,
this
是一个Stat
对象(我们很快就会定义) that
是另一个Stat
对象 thos
这是一个Rank
的对象(我们定义为好) them
是另一个Rank
对象 要继续,我们将Stat
指定为具有三个属性的对象,
makes
这是一个数字 attempts
是另一个数字 pct
是另一个号码 最后, Rank
是一个具有两个属性的对象,
value
是一个数字 rank
是另一个数字 我们现在可以将arrayOfPeopleStats
视为一个项目数组,其中每个项目都是Person类型。 为了组合类似的类型,我们定义了一种create
类型值的方法以及concat
(添加)它们的方法。
我将首先定义较小的类型, Stat
-
const Stat =
{ create: (makes, attempts) =>
({ makes, attempts, pct: makes / attempts })
, concat: (s1, s2) =>
Stat .create
( s1.makes + s2.makes
, s1.attempts + s2.attempts
)
}
Rank
类型类似。 你说rank
的总和是“无所谓” ,但这表明你必须做出选择。 您可以选择一个或另一个,将两个值一起添加,或完全添加其他选项。 在此实现中,我们选择最高rank
值 -
const Rank =
{ create: (value, rank) =>
({ value, rank })
, concat: (r1, r2) =>
Rank .create
( r1.value + r2.value
, Math .max (r1.rank, r2.rank)
)
}
最后,我们实施Person
。 我改名this
, that
, thos
, them
到a
, b
, c
, d
,因为this
是在JavaScript中一个特殊的变量,我认为它使例子更容易一些遵循; 无论如何,你问题中的数据看起来都是嘲笑的。 这里需要注意的是Person.concat
函数依赖于Stat.concat
和Rank.concat
,保持每个类型的组合逻辑很好地分开 -
const Person =
{ create: (a, b, c, d) =>
({ a, b, c, d })
, concat: (p1, p2) =>
Person .create
( Stat .concat (p1.a, p2.a)
, Stat .concat (p1.b, p2.b)
, Rank .concat (p1.c, p2.c)
, Rank .concat (p1.d, p2.d)
)
}
现在要组合数组中的所有person
项,只需减少使用Person.concat
操作 -
arrayOfPeopleStat .reduce (Person.concat)
// { a: { makes: 29, attempts: 44, pct: 0.6590909090909091 }
// , b: { makes: 29, attempts: 36, pct: 0.8055555555555556 }
// , c: { value: 26, rank: 40 }
// , d: { value: 31, rank: 32 }
// }
注意,当你有一个很好定义的数据构造函数时,使用{ ... }
手工编写数据很麻烦且容易出错。 而是使用您的类型的create
功能 -
var arrayOfPeopleStat =
[ Person .create
( Stat .create (10, 15)
, Stat .create (12, 16)
, Rank .create (10, 24)
, Rank .create (15, 11)
)
, Person .create
( Stat .create (5, 15)
, Stat .create (10, 12)
, Rank .create (4, 40)
, Rank .create (3, 32)
)
, Person .create
( Stat .create (14, 14)
, Stat .create (7, 8)
, Rank .create (12, 12)
, Rank .create (13, 10)
)
]
使用create
函数而不是使用{ ... }
手动写出数据的另一个好处是构造函数可以为您提供帮助。 请注意如何自动为Stat
计算pct
。 这可以防止有人编写无效的统计信息,例如{ makes: 1, attempts: 2, pct: 3 }
,其中pct
属性不等于makes/attempts
。 如果此数据的接收方未检查,我们无法确保pct
属性的完整性。 在另一方面利用我们的构造, Stat.create
只接受makes
和attempts
和pct
领域是自动计算的,保证正确的pct
值。
构造函数还可以阻止某人创建会导致错误的stat,例如Stat .create (10, 0)
,它会尝试为pct
属性计算10/0
(除零错误) -
const Stat =
{ create: (makes = 0, attempts = 0) =>
attempts === 0
? { makes, attempts, pct: 0 } // instead of throwing error
: { makes, attempts, pct: makes / attempts } // safe to divide
, concat: ...
}
最终,这些选择取决于您。 另一位程序员可能会在这种情况下支持错误。 如 -
Stat .create (10, 0)
// Error: invalid Stat. makes (10) cannot be greater than attempts (0). attempts cannot be zero.
reduce
的结果当然是相同的 -
arrayOfPeopleStat .reduce (Person.concat)
// { a: { makes: 29, attempts: 44, pct: 0.6590909090909091 }
// , b: { makes: 29, attempts: 36, pct: 0.8055555555555556 }
// , c: { value: 26, rank: 40 }
// , d: { value: 31, rank: 32 }
// }
展开下面的代码段以在您自己的浏览器中验证结果 -
const Stat = { create: (makes, attempts) => ({ makes, attempts, pct: makes / attempts }) , concat: (s1, s2) => Stat .create ( s1.makes + s2.makes , s1.attempts + s2.attempts ) } const Rank = { create: (value, rank) => ({ value, rank }) , concat: (r1, r2) => Rank .create ( r1.value + r2.value , Math .max (r1.rank, r2.rank) ) } const Person = { create: (a, b, c, d) => ({ a, b, c, d }) , concat: (p1, p2) => Person .create ( Stat .concat (p1.a, p2.a) , Stat .concat (p1.b, p2.b) , Rank .concat (p1.c, p2.c) , Rank .concat (p1.d, p2.d) ) } var data = [ Person .create ( Stat .create (10, 15) , Stat .create (12, 16) , Rank .create (10, 24) , Rank .create (15, 11) ) , Person .create ( Stat .create (5, 15) , Stat .create (10, 12) , Rank .create (4, 40) , Rank .create (3, 32) ) , Person .create ( Stat .create (14, 14) , Stat .create (7, 8) , Rank .create (12, 12) , Rank .create (13, 10) ) ] console .log (data .reduce (Person.concat)) // { a: { makes: 29, attempts: 44, pct: 0.6590909090909091 } // , b: { makes: 29, attempts: 36, pct: 0.8055555555555556 } // , c: { value: 26, rank: 40 } // , d: { value: 31, rank: 32 } // }
但是如果data
是空数组会发生什么?
[] .reduce (Person.concat)
// Uncaught TypeError: Reduce of empty array with no initial value
如果我们的代码中的一段代码适用于某些数组但会破坏其他数组,那就太糟糕了。 数组用于表示零个或多个值,因此我们希望涵盖所有基础,包括零统计的情况。 为其输入的所有可能值定义的函数是一个总函数 ,我们尽可能地以这种方式编写程序。
错误消息为修复提供了智慧; 我们必须提供初始值 。 比如0
in -
const add = (x, y) => x + y console .log ( [] .reduce (add, 0) // 0 , [ 1, 2, 3 ] .reduce (add, 0) // 6 )
使用初始值,即使数组为空,我们也总是收到正确的结果。 为了添加数字,我们使用零的初始值,因为它是标识元素 。 您可以将此视为一种空值。
如果我们试图reduce
Person类型的数组,那么Person的初始值是多少?
arrayOfPeopleStat .reduce (Person.concat, ???)
如果可能,我们为每个类型定义一个空值。 我们将从Stat
开始 -
const Stat =
{ empty:
{ makes: 0
, attempts: 0
, pct: 0
}
, create: ...
, concat: ...
}
接下来我们会做Rank
-
const Rank =
{ empty:
{ value: 0
, rank: 0
}
, create: ...
, concat: ...
}
同样,我们将Person视为复合数据 ,即由其他数据构建的数据。 我们依靠Stat.empty
和Rank.empty
来创建Person.empty
-
const Person =
{ empty:
{ a: Stat.empty
, b: Stat.empty
, c: Rank.empty
, d: Rank.empty
}
, create: ...
, concat: ...
}
现在我们可以指定Person.empty
作为初始值,并防止加剧错误弹出 -
arrayOfPeopleStat .reduce (Person.concat, Person.empty)
// { a: { makes: 29, attempts: 44, pct: 0.6590909090909091 }
// , b: { makes: 29, attempts: 36, pct: 0.8055555555555556 }
// , c: { value: 26, rank: 40 }
// , d: { value: 31, rank: 32 }
// }
[] .reduce (Person.concat, Person.empty)
// { a: { makes: 0, attempts: 0, pct: 0 }
// , b: { makes: 0, attempts: 0, pct: 0 }
// , c: { value: 0, rank: 0 }
// , d: { value: 0, rank: 0 }
// }
作为奖励,您现在对幺半群有了基本的了解。
在map
和reduce
之间进行决定时,您可以认为map
将返回一个与原始 array
长度相同的新array
。 这不是你想要的,所以你不能使用它。 实际上,您希望将数组减少为对象。 所以只需使用reduce
:
关于减少它的功能,这里有一个可能的:
var arrayOfPeopleStats=[person1={this:{makes:10,attempts:15,pct:.666667},that:{makes:12,attempts:16,pct:.75},thos:{value:10,rank:24},them:{value:15,rank:11}},person2={this:{makes:5,attempts:15,pct:.333333},that:{makes:10,attempts:12,pct:.83333},thos:{value:4,rank:40},them:{value:3,rank:32}},person3={this:{makes:14,attempts:14,pct:1},that:{makes:7,attempts:8,pct:.875},thos:{value:12,rank:12},them:{value:13,rank:10}}]; const resp = Object.values(arrayOfPeopleStats).reduce((obj, { this: localThis, that, thos, them }) => { obj.this = { makes: obj.this.makes + localThis.makes, attempts: obj.this.attempts + localThis.attempts }; obj.this.pct = obj.this.makes / obj.this.attempts; obj.that = { makes: obj.that.makes + that.makes, attempts: obj.that.attempts + that.attempts }; obj.that.pct = obj.that.makes / obj.that.attempts; obj.thos = { value: obj.thos.value + thos.value, rank: obj.thos.rank + thos.rank }; obj.them = { value: obj.them.value + them.value, rank: obj.thos.rank + them.rank }; return obj; }) const formattedResp = [{ allPeople: resp }] console.log(formattedResp)
一旦它与this
关键字冲突,我建议您不要将this
用作属性名称。
arrayOfPeopleStats.reduce((a, v) => {
a.allPeople.this = {
makes: a.allPeople.this.makes + v.this,
attempts: a.allPeople.this.attempts + v.this.attempts,
};
a.allPeople.that = {
makes: a.allPeople.that.makes + v.that.makes,
attempts: a.allPeople.that.attempts + v.that.attempts,
};
a.allPeople.thos = {
value: a.allPeople.thos.value + v.thos.value,
};
a.allPeople.them = {
value: a.allPeople.them.value + v.them.value,
};
a.allPeople.this.pct = a.allPeople.this.makes / a.allPeople.this.attempts
a.allPeople.that.pct = a.allPeople.that.makes / a.allPeople.that.attempts
return a;
}, {allPeople: {
this: { makes: 0, attempts: 0, pct: 0 },
that: { makes: 0, attempts: 0, pct: 0 },
thos: { value: 0, rank: 0 },
them: { value: 0, rank: 0 }
}
}});
我已经忽略了排名,因为你已经说过没关系,它总是会出现上述解决方案。 我还选择不像你在你想要的结果中那样将结果对象包装在一个数组中。 有关此API的说明,请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.