配列のプロパティ値を合計するより良い方法


182

私はこのようなものを持っています:

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

配列が1つだけの場合は簡単ですが、合計したいプロパティ名が異なる他の配列があります。

次のようなことができれば幸いです。

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

しかし、私はこれを次のように将来的に再利用できるようにする方法を知りません:

$scope.someArray.Sum({ someProperty });

回答

@ gruff-bunnyの提案を使用することにしたので、ネイティブオブジェクト(配列)のプロトタイピング避けました

配列を検証する彼の回答に少し変更を加えただけで、合計する値が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
あなたの投稿も回答として答え
Ken Kin

回答:


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に感謝します。gruff-bunnyが答えたように、array.reduceを使用して実装を変更しました。
nramirez 2014

あなたは古いブラウザをサポートする必要がある場合は機能を減らすポリフィルする必要があるかもしれません:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...は
bottens

Array.prototype将来的にコードが壊れる可能性があるため、拡張しないでください。なぜネイティブオブジェクトの拡張が悪い習慣なのですか?
str

@bottens配列にnullオブジェクトがある場合はどうなりますか?
オビー

228

私はこの質問に受け入れられる答えがあることを知っていますが、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');コントローラー機能の最初に置くことは可能ですか?
Mo.

合計関数が作成された後であれば、はい。
Gruff Bunny

14
2015年10月6日の反対投票の理由は何ですか?回答に問題がある場合は、コメントを追加してください。修正します。匿名の反対投票から学ぶ人はいません。
Gruff Bunny

配列内のオブジェクトの1つにプロップが存在しない(未定義)場合、これによりNaNが発生する可能性があります。これが最良のチェックかどうかはわかりませんが、私の場合はうまくいきました:function(items、prop){return items.reduce(function(a、b){if(!isNaN(b [prop])){ return a + b [prop];} else {return a}}、0); }
claytronicon

129

もう1つの見方をすれば、これは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 2017

4
prevは、reduce関数の変数の名前についてのみ議論します。私にとって、これは配列の前の値を取得していることを意味します。しかし、実際には、それは以前のすべての値の減少です。私はaccumulatoraggregatorまたはsumSoFarわかりやすくするために。
carpiediem

92

私は常にプロトタイプメソッドの変更とライブラリの追加を避けているので、これが私の解決策です。

配列の縮小プロトタイプメソッドの使用で十分です

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

3
2番目のパラメーター(の初期値を決定するa)は、reduceオブジェクトで使用するときにトリックを実行します。最初の反復でaは、items配列の最初のオブジェクトではなく、になります0
Parziphal

機能として:const sumBy = (items, prop) => items.reduce((a, b) => +a + +b[prop], 0); 使用法:sumBy(traveler, 'Amount') // => 235
Rafi

2
古い学校のプログラマーとして、reduce構文は私にはまったく鈍く見えます。私の脳は今でも「let total = 0; items.forEach((item) => { total += item.price; });
自然

1
@AndrWeisR私はあなたのソリューションが一番好きです。また、ネイティブの使用は、それが何を意味するのかほとんど説明されないような流行語であるためです。私はあなたのソリューションに基づいてさらに基本的なコード行を作成しましたが、うまくいきました。let total = 0; items.forEach(item => total += item.price)
Ian Poston Framer

22

私はこれに2セント落とすと思いました。これは、外部変数に依存せず、常に純粋に機能する必要がある操作の1つです。いくつかはすでに良い答えを出しました、使用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

19

次のことができます。

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

13

それは私のために働いているTypeScriptJavaScript

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関数があります。以下のスニペットは、合計する属性が「値」です。

_.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');
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.