对数组中的属性值求和的更好方法

IT技术 javascript arrays prototype-programming
2021-01-16 13:41:37

我有这样的事情:

$scope.traveler = [
            {  description: 'Senior', Amount: 50},
            {  description: 'Senior', Amount: 50},
            {  description: 'Adult', Amount: 75},
            {  description: 'Child', Amount: 35},
            {  description: 'Infant', Amount: 25 },
];

现在要获得这个数组的总数量,我正在做这样的事情:

$scope.totalAmount = function(){
       var total = 0;
       for (var i = 0; i < $scope.traveler.length; i++) {
              total = total + $scope.traveler[i].Amount;
            }
       return total;
}

当只有一个数组时很容易,但我有其他数组具有不同的属性名称,我想对其进行求和。

如果我能做这样的事情,我会更开心:

$scope.traveler.Sum({ Amount });

但是我不知道如何以一种将来可以像这样重复使用它的方式来完成它:

$scope.someArray.Sum({ someProperty });
6个回答

我知道这个问题有一个公认的答案,但我想我会用一个使用array.reduce的替代方法,看到对数组求和是reduce的规范示例:

$scope.sum = function(items, prop){
    return items.reduce( function(a, b){
        return a + b[prop];
    }, 0);
};

$scope.travelerTotal = $scope.sum($scope.traveler, 'Amount');

Fiddle

精彩的答案,是否可以放在$scope.travelerTotal = $scope.sum($scope.traveler, 'Amount');控制器功能的开头?
2021-03-14 13:41:37
2015 年 10 月 6 日投反对票的任何原因?如果答案有问题,请添加评论,我会更正。没有人会从匿名投票中学习。
2021-03-24 13:41:37
如果在数组中的对象之一中不存在(未定义)道具,则这可能导致 NaN。我不知道这是否是最好的检查,但它适用于我的情况: function(items, prop) { return items.reduce(function(a, b) { if (!isNaN(b[prop])) { return a + b[prop]; } else { return a } }, 0); }
2021-03-24 13:41:37
是的,如果它是在 sum 函数创建之后出现的。
2021-04-06 13:41:37

再想一想,这就是nativeJavaScript 的功能Map和目的Reduce(Map 和 Reduce 在许多语言中都是强大的)。

var traveler = [{description: 'Senior', Amount: 50},
                {description: 'Senior', Amount: 50},
                {description: 'Adult', Amount: 75},
                {description: 'Child', Amount: 35},
                {description: 'Infant', Amount: 25}];

function amount(item){
  return item.Amount;
}

function sum(prev, next){
  return prev + next;
}

traveler.map(amount).reduce(sum);
// => 235;

// or use arrow functions
traveler.map(item => item.Amount).reduce((prev, next) => prev + next);

注意:通过制作单独的小函数,我们可以再次使用它们。

// Example of reuse.
// Get only Amounts greater than 0;

// Also, while using Javascript, stick with camelCase.
// If you do decide to go against the standards, 
// then maintain your decision with all keys as in...

// { description: 'Senior', Amount: 50 }

// would be

// { Description: 'Senior', Amount: 50 };

var travelers = [{description: 'Senior', amount: 50},
                {description: 'Senior', amount: 50},
                {description: 'Adult', amount: 75},
                {description: 'Child', amount: 35},
                {description: 'Infant', amount: 0 }];

// Directly above Travelers array I changed "Amount" to "amount" to match standards.

function amount(item){
  return item.amount;
}

travelers.filter(amount);
// => [{description: 'Senior', amount: 50},
//     {description: 'Senior', amount: 50},
//     {description: 'Adult', amount: 75},
//     {description: 'Child', amount: 35}];
//     Does not include "Infant" as 0 is falsey.
我确实认为这是最好的anwser。
2021-03-11 13:41:37
注意,在分离.MAP(第(有争议)优势)和降低()调用具有可组合的函数来在因为两者的性能降低的成本.map().reduce()将分别通过迭代整个阵列。如果您正在处理大量数据,最好使用单个 for 循环而不是单独的数组方法
2021-03-14 13:41:37
return Number(item.Amount); 只检查号码
2021-03-28 13:41:37
我只会与prevreduce 函数中的变量名称争论。对我来说,这意味着您正在获取数组中的先前值。但实际上这是所有先前值的减少。我喜欢accumulatoraggregator或者sumSoFar为了清楚起见。
2021-04-09 13:41:37

更新答案

由于向 Array 原型添加函数的所有缺点,我正在更新此答案以提供一种替代方法,使语法与问题中最初请求的语法保持相似。

class TravellerCollection extends Array {
    sum(key) {
        return this.reduce((a, b) => a + (b[key] || 0), 0);
    }
}
const traveler = new TravellerCollection(...[
    {  description: 'Senior', Amount: 50},
    {  description: 'Senior', Amount: 50},
    {  description: 'Adult', Amount: 75},
    {  description: 'Child', Amount: 35},
    {  description: 'Infant', Amount: 25 },
]);

console.log(traveler.sum('Amount')); //~> 235

原答案

由于它是一个数组,您可以向 Array 原型添加一个函数。

traveler = [
    {  description: 'Senior', Amount: 50},
    {  description: 'Senior', Amount: 50},
    {  description: 'Adult', Amount: 75},
    {  description: 'Child', Amount: 35},
    {  description: 'Infant', Amount: 25 },
];

Array.prototype.sum = function (prop) {
    var total = 0
    for ( var i = 0, _len = this.length; i < _len; i++ ) {
        total += this[i][prop]
    }
    return total
}

console.log(traveler.sum("Amount"))

小提琴:http : //jsfiddle.net/9BAmj/

谢谢@sp00m 现在我已经用 array.reduce 改变了我的实现,就像 gruff-bunny 回答一样。
2021-03-28 13:41:37
请不要扩展,Array.prototype因为它将来可能会破坏您的代码。为什么扩展本机对象是一种不好的做法?.
2021-03-30 13:41:37
如果您需要支持旧浏览器,您可能需要对 reduce 功能进行 polyfill:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-04-05 13:41:37
@bottens 如果数组中有空对象怎么办?
2021-04-08 13:41:37

我总是避免改变原型方法和添加库,所以这是我的解决方案:

使用reduce Array原型方法就足够了

// + operator for casting to Number
items.reduce((a, b) => +a + +b.price, 0);
作为函数:const sumBy = (items, prop) => items.reduce((a, b) => +a + +b[prop], 0); 用法:sumBy(traveler, 'Amount') // => 235
2021-03-10 13:41:37
作为一个老派的程序员,reduce语法对我来说看起来很钝。我的大脑仍然“自然地”更好地理解这一点:let total = 0; items.forEach((item) => { total += item.price; });
2021-03-11 13:41:37
第二个参数(确定 的初始值a)在reduce与对象一起使用时起到了作用:在第一次迭代中,a不会是items数组的第一个对象,而是0
2021-04-04 13:41:37
@AndrWeisR 我最喜欢你的解决方案。还使用原生,因为它是一个流行词,很难解释它的含义。我根据您的解决方案编写了更基本的代码行,并且效果很好。let total = 0; items.forEach(item => total += item.price)
2021-04-07 13:41:37

使用 reduce 和解构来求和 Amount:

const traveler = [
  { description: 'Senior', Amount: 50 },
  { description: 'Senior', Amount: 50 },
  { description: 'Adult', Amount: 75 },
  { description: 'Child', Amount: 35 },
  { description: 'Infant', Amount: 25 },
];

console.log(traveler.reduce((n, {Amount}) => n + Amount, 0))
这应该是最佳答案
2021-03-14 13:41:37
简单易行,我选择的答案
2021-03-23 13:41:37
简单而强大的方法 - 应该是所有答案的首选。谢谢@patrick
2021-04-05 13:41:37