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


179

我有这样的事情:

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

回答

我决定使用@ gruff-bunny建议,因此避免原型原型本机对象(数组)

我只是对他的答案做了一些修改,以验证数组,并且sum的值不为null,这是我的最终实现:

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

2
您可以将您的答案发布答案
肯健

Answers:


124

更新的答案

由于将函数添加到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/


1
谢谢@ sp00m现在我已经用array.reduce更改了我的实现,就像gruff-bunny回答了一样。
nramirez 2014年

如果您需要支持较旧的浏览器,则可能需要填充reduce函数:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…–
bottens

请不要扩展,Array.prototype因为将来可能会破坏您的代码。为什么扩展本机对象是一种不好的做法?
海峡

226

我知道这个问题的答案是可以接受的,但是我想我可以使用一个使用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');控制器功能的开头吗?
2014年

是的,如果它在创建求和函数之后出现。
Gruff Bunny 2014年

13
有什么理由在2015年10月6日投票否决?如果答案有问题,请添加评论,我会予以纠正。没有人能从匿名的投票中学习。
Gruff Bunny

如果prop在数组中的对象之一中不存在(未定义),则可能导致NaN。我不知道这是否是最好的检查,但是它适用于我的情况:function(items,prop){return items.reduce(function(a,b){if(!isNaN(b [prop])){返回a + b [prop];} else {return a}},0); }
claytronicon

127

又一个拿,这是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.

4
return Number(item.Amount);仅检查号码
diEcho

4
我只会prev在reduce函数中使用变量的名称。对我来说,这意味着您要在数组中获取先前的值。但这实际上是所有先前值的减少。我喜欢accumulatoraggregator或者sumSoFar为了清楚起见。
carpiediem

92

我总是避免更改原型方法并添加库,因此这是我的解决方案:

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

// + operator for casting to Number
items.reduce((a, b) => +a + +b.price, 0);

3
与对象一起a使用时,第二个参数(确定的初始值)可以解决问题reduce:在第一个迭代中,它不是数组a的第一个对象items,而是对象0
Parziphal

功能:const sumBy = (items, prop) => items.reduce((a, b) => +a + +b[prop], 0); 用法:sumBy(traveler, 'Amount') // => 235
拉菲

2
作为一名高中程序员,该reduce语法在我看来似乎很晦涩。我的大脑仍然“天然地”更好地理解了这一点:let total = 0; items.forEach((item) => { total += item.price; });
AndrWeisR

1
@AndrWeisR我最喜欢您的解决方案。同样,使用native作为一个时髦的词,几乎无法解释其含义。我根据您的解决方案编写了更基本的代码行,并且效果很好。let total = 0; items.forEach(item => total += item.price)
伊恩·波斯顿·查弗

21

我以为我会为此付出两分钱:这是应该始终纯粹是功能性的操作之一,而不是依赖于任何外部变量。一些人已经给出了很好的答案,使用reduce便是这里的方法。

由于我们大多数人已经可以负担得起使用ES2015语法,这是我的主张:

const sumValues = (obj) => Object.keys(obj).reduce((acc, value) => acc + obj[value], 0);

我们正在将它变为不变的函数。什么reduce是做在这里很简单:开始的值0的蓄电池,以及当前环状项的值添加到它。

是的,用于函数式编程和ES2015!:)


21

替代方法,以提高可读性并使用MapReduce

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

const sum = traveler
  .map(item => item.amount)
  .reduce((prev, curr) => prev + curr, 0);

可重用功能:

const calculateSum = (obj, field) => obj
  .map(items => items.attributes[field])
  .reduce((prev, curr) => prev + curr, 0);

完美伴侣,“ + 1”
Mayank Pandeyz

使用减少默认值.reduce(*** => *** + ***, 0);其他人缺少“ +1”
FDisk

18

您可以执行以下操作:

$scope.traveler.map(o=>o.Amount).reduce((a,c)=>a+c);

13

它在TypeScript和中为我工作JavaScript

let lst = [
     { description:'Senior', price: 10},
     { description:'Adult', price: 20},
     { description:'Child', price: 30}
];
let sum = lst.map(o => o.price).reduce((a, c) => { return a + c });
console.log(sum);

我希望是有用的。


4

我不确定这已被提及。但是有一个lodash函数。以下是其中value是您求和属性的代码片段是“ value”。

_.sumBy(objects, 'value');
_.sumBy(objects, function(o) { return o.value; });

两者都会起作用。



1

这是使用ES6箭头功能的单线。

const sumPropertyValue = (items, prop) => items.reduce((a, b) => a + b[prop], 0);

// usage:
const cart_items = [ {quantity: 3}, {quantity: 4}, {quantity: 2} ];
const cart_total = sumPropertyValue(cart_items, 'quantity');

1

如何使用JavaScript求和对象数组

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

const traveler = [
    {  description: 'Senior', Amount: 50},
    {  description: 'Senior', Amount: 50},
    {  description: 'Adult', Amount: 75},
    {  description: 'Child', Amount: 35},
    {  description: 'Infant', Amount: 25 },
];
function sum(arrayData, key){
   return arrayData.reduce((a,b) => {
  return {Amount : a.Amount + b.Amount}
})
}
console.log(sum(traveler))
`


1

从对象数组

function getSum(array, column)
  let values = array.map((item) => parseInt(item[column]) || 0)
  return values.reduce((a, b) => a + b)
}

foo = [
  { a: 1, b: "" },
  { a: null, b: 2 },
  { a: 1, b: 2 },
  { a: 1, b: 2 },
]

getSum(foo, a) == 3
getSum(foo, b) == 6

0

我已经在使用jQuery。但我认为它足够直观,只需:

var total_amount = 0; 
$.each(traveler, function( i, v ) { total_amount += v.Amount ; });

这基本上只是@akhouri答案的简写版本。


0

这是我发现更灵活的解决方案:

function sumOfArrayWithParameter (array, parameter) {
  let sum = null;
  if (array && array.length > 0 && typeof parameter === 'string') {
    sum = 0;
    for (let e of array) if (e && e.hasOwnProperty(parameter)) sum += e[parameter];
  }
  return sum;
}

要获得总和,只需像这样使用它:

let sum = sumOfArrayWithParameter(someArray, 'someProperty');
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.