Wie summiere ich die Werte eines JavaScript-Objekts?

82

Ich möchte die Werte eines Objekts summieren.

Ich bin an Python gewöhnt, wo es nur sein würde:

sample = { 'a': 1 , 'b': 2 , 'c':3 };
summed =  sum(sample.itervalues())     

Der folgende Code funktioniert, aber es ist viel Code:

function obj_values(object) {
  var results = [];
  for (var property in object)
    results.push(object[property]);
  return results;
}

function list_sum( list ){
  return list.reduce(function(previousValue, currentValue, index, array){
      return previousValue + currentValue;
  });
}

function object_values_sum( obj ){
  return list_sum(obj_values(obj));
}

var sample = { a: 1 , b: 2 , c:3 };
var summed =  list_sum(obj_values(a));
var summed =  object_values_sum(a)

Vermisse ich etwas Offensichtliches oder ist das einfach so?

Jonathan Vanasco
quelle

Antworten:

70

Sie könnten alles in einer Funktion zusammenfassen:

function sum( obj ) {
  var sum = 0;
  for( var el in obj ) {
    if( obj.hasOwnProperty( el ) ) {
      sum += parseFloat( obj[el] );
    }
  }
  return sum;
}
    
var sample = { a: 1 , b: 2 , c:3 };
var summed = sum( sample );
console.log( "sum: "+summed );


Zum Spaß gibt es hier eine weitere Implementierung mit Object.keys()und Array.reduce()(Browserunterstützung sollte kein großes Problem mehr sein):

function sum(obj) {
  return Object.keys(obj).reduce((sum,key)=>sum+parseFloat(obj[key]||0),0);
}
let sample = { a: 1 , b: 2 , c:3 };

console.log(`sum:${sum(sample)}`);

Dies scheint jedoch viel langsamer zu sein: jsperf.com

Sirko
quelle
return sum + parseFloat (obj [key] || 0), um die Falsey- oder Null / Blank-Werte zu überprüfen
sumit
Großartige Arbeit, die den Leistungsunterschied zwischen den Lösungen hervorhebt. Während das Object.keys().reduceAussehen so viel eleganter ist, ist es 60% langsamer.
micnguyen
94

So einfach kann das sein:

const sumValues = obj => Object.values(obj).reduce((a, b) => a + b);

MDN zitieren:

Die Object.values()Methode gibt ein Array der eigenen aufzählbaren Eigenschaftswerte eines bestimmten Objekts in derselben Reihenfolge zurück, die von einer for...inSchleife bereitgestellt wird (der Unterschied besteht darin, dass eine For-In-Schleife auch Eigenschaften in der Prototypkette auflistet).

von Object.values()auf MDN

Die reduce()Methode wendet eine Funktion auf einen Akkumulator und jeden Wert des Arrays (von links nach rechts) an, um ihn auf einen einzelnen Wert zu reduzieren.

von Array.prototype.reduce()auf MDN

Sie können diese Funktion folgendermaßen verwenden:

sumValues({a: 4, b: 6, c: -5, d: 0}); // gives 5

Beachten Sie, dass dieser Code einige ECMAScript-Funktionen verwendet, die von einigen älteren Browsern (wie dem IE) nicht unterstützt werden. Möglicherweise müssen Sie Babel verwenden , um Ihren Code zu kompilieren.

Michał Perłakowski
quelle
3
Dies erfordert , dass Sie eine 60K - Bibliothek ziehen nur zu haben Object.values(), die mit einer polyfilled wird forSchleife auf jedem Browser neben Firefox. Auch ohne Polyfill ist es forfür mich 4x langsamer als eine normale Schleife.
Blender
10
@Blender Sie müssen Babel trotzdem verwenden, wenn Sie eine der neuen ECMAScript-Funktionen verwenden und dennoch ältere Browser unterstützen möchten . Wenn jemand diese Frage zum Beispiel nach 2 Jahren besucht, werden moderne Browser wahrscheinlich Object.values()bis zu diesem Zeitpunkt implementiert .
Michał Perłakowski
Die akzeptierte Antwort hat einen sehr ähnlichen Ansatz, aber die Funktion, an die übergeben wurde, reducescheint etwas narrensicherer zu sein. Haben Sie das Parsen absichtlich ausgelassen?
Cerbrus
@ Cerbrus Ich habe angenommen, dass alle Werte in diesem Objekt Zahlen sind.
Michał Perłakowski
10
@Blender Es scheint, ich hatte Recht - anderthalb Jahre sind vergangen und werden Object.values()von allen modernen Browsern unterstützt .
Michał Perłakowski
24

Wenn Sie lodash verwenden, können Sie so etwas tun

_.sum(_.values({ 'a': 1 , 'b': 2 , 'c':3 })) 
Leon
quelle
20

Eine reguläre forSchleife ist ziemlich kurz:

var total = 0;

for (var property in object) {
    total += object[property];
}

Möglicherweise müssen Sie hinzufügen, object.hasOwnPropertywenn Sie den Prototyp geändert haben.

Mixer
quelle
13

Ehrlich gesagt, würde ich angesichts unserer "modernen Zeit", wann immer möglich, einen funktionalen Programmieransatz wählen, wie zum Beispiel:

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

Unser Akkumulator acc, beginnend mit einem Wert von 0, akkumuliert alle Schleifenwerte unseres Objekts. Dies hat den zusätzlichen Vorteil, dass es nicht von internen oder externen Variablen abhängt. Es ist eine konstante Funktion, damit es nicht versehentlich überschrieben wird ... Gewinn für ES2015!

Ricardo Magalhães
quelle
12

Gibt es einen Grund, warum Sie nicht nur eine einfache for...inSchleife verwenden?

var sample = { a: 1 , b: 2 , c:3 };
var summed = 0;

for (var key in sample) {
    summed += sample[key];
};

http://jsfiddle.net/vZhXs/

jbabey
quelle
11

Jetzt können Sie die reduceFunktion nutzen und die Summe erhalten.

const object1 = { 'a': 1 , 'b': 2 , 'c':3 }

console.log(Object.values(object1).reduce((a, b) => a + b, 0));

Krishnadas PC
quelle
1

Ich bin etwas verspätet gegenüber der Party, aber wenn Sie eine robustere und flexiblere Lösung benötigen, dann ist hier mein Beitrag. Wenn Sie nur eine bestimmte Eigenschaft in einer Kombination aus verschachteltem Objekt und Array summieren und andere Aggregatmethoden ausführen möchten, ist hier eine kleine Funktion, die ich für ein React-Projekt verwendet habe:

var aggregateProperty = function(obj, property, aggregate, shallow, depth) {
    //return aggregated value of a specific property within an object (or array of objects..)

    if ((typeof obj !== 'object' && typeof obj !== 'array') || !property) {
        return;
    }

    obj = JSON.parse(JSON.stringify(obj)); //an ugly way of copying the data object instead of pointing to its reference (so the original data remains unaffected)
    const validAggregates = [ 'sum', 'min', 'max', 'count' ];
    aggregate = (validAggregates.indexOf(aggregate.toLowerCase()) !== -1 ? aggregate.toLowerCase() : 'sum'); //default to sum

    //default to false (if true, only searches (n) levels deep ignoring deeply nested data)
    if (shallow === true) {
        shallow = 2;
    } else if (isNaN(shallow) || shallow < 2) {
        shallow = false;
    }

    if (isNaN(depth)) {
        depth = 1; //how far down the rabbit hole have we travelled?
    }

    var value = ((aggregate == 'min' || aggregate == 'max') ? null : 0);
    for (var prop in obj) {
        if (!obj.hasOwnProperty(prop)) {
            continue;
        }

        var propValue = obj[prop];
        var nested = (typeof propValue === 'object' || typeof propValue === 'array');
        if (nested) {
            //the property is an object or an array

            if (prop == property && aggregate == 'count') {
                value++;
            }

            if (shallow === false || depth < shallow) {
                propValue = aggregateProperty(propValue, property, aggregate, shallow, depth+1); //recursively aggregate nested objects and arrays
            } else {
                continue; //skip this property
            }
        }

        //aggregate the properties value based on the selected aggregation method
        if ((prop == property || nested) && propValue) {
            switch(aggregate) {
                case 'sum':
                    if (!isNaN(propValue)) {
                        value += propValue;
                    }
                    break;
                case 'min':
                    if ((propValue < value) || !value) {
                        value = propValue;
                    }
                    break;
                case 'max':
                    if ((propValue > value) || !value) {
                        value = propValue;
                    }
                    break;
                case 'count':
                    if (propValue) {
                        if (nested) {
                            value += propValue;
                        } else {
                            value++;
                        }
                    }
                    break;
            }
        }
    }

    return value;
}

Es ist rekursiv, nicht ES6, und es sollte in den meisten semi-modernen Browsern funktionieren. Sie verwenden es so:

const onlineCount = aggregateProperty(this.props.contacts, 'online', 'count');

Parameteraufschlüsselung:

obj = entweder ein Objekt oder eine Array-
Eigenschaft = die Eigenschaft innerhalb der verschachtelten Objekte / Arrays, für die Sie die Aggregatmethode für
Aggregat ausführen möchten = die Aggregatmethode (Summe, Min, Max oder Anzahl)
flach = kann entweder auf true / gesetzt werden false oder eine numerische
Werttiefe = sollte null oder undefiniert bleiben (wird verwendet, um die nachfolgenden rekursiven Rückrufe zu verfolgen).

Flach kann verwendet werden, um die Leistung zu verbessern, wenn Sie wissen, dass Sie keine tief verschachtelten Daten durchsuchen müssen. Zum Beispiel, wenn Sie das folgende Array hatten:

[
    {
        id: 1,
        otherData: { ... },
        valueToBeTotaled: ?
    },
    {
        id: 2,
        otherData: { ... },
        valueToBeTotaled: ?
    },
    {
        id: 3,
        otherData: { ... },
        valueToBeTotaled: ?
    },
    ...
]

Wenn Sie vermeiden möchten, die Eigenschaft otherData zu durchlaufen, da der Wert, den Sie aggregieren möchten, nicht so tief verschachtelt ist, können Sie flach auf true setzen.


quelle
1

Verwenden Sie Lodash

 import _ from 'Lodash';
 
 var object_array = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}];
 
 return _.sumBy(object_array, 'c')
 
 // return => 9

Ismael Soschinski
quelle
1

let prices = {
  "apple": 100,
  "banana": 300,
  "orange": 250
};

let sum = 0;
for (let price of Object.values(prices)) {
  sum += price;
}

alert(sum)

Sudam Dissanayake
quelle
0

Ich bin auf diese Lösung von @jbabey gestoßen, als ich versucht habe, ein ähnliches Problem zu lösen. Mit einer kleinen Modifikation habe ich es richtig gemacht. In meinem Fall sind die Objektschlüssel Zahlen (489) und Zeichenfolgen ("489"). Um dies zu lösen, wird jeder Schlüssel analysiert. Der folgende Code funktioniert:

var array = {"nR": 22, "nH": 7, "totB": "2761", "nSR": 16, "htRb": "91981"}
var parskey = 0;
for (var key in array) {
    parskey = parseInt(array[key]);
    sum += parskey;
};
return(sum);
Olu Adabonyan
quelle
0

Ein Ramda One Liner:

import {
 compose, 
 sum,
 values,
} from 'ramda'

export const sumValues = compose(sum, values);

Verwenden: const summed = sumValues({ 'a': 1 , 'b': 2 , 'c':3 });

Damian Green
quelle
0

Wir können Objekte mit dem Schlüsselwort in iterieren und jede arithmetische Operation ausführen.

// input
const sample = {
    'a': 1,
    'b': 2,
    'c': 3
};

// var
let sum = 0;

// object iteration
for (key in sample) {
    //sum
    sum += (+sample[key]);
}
// result
console.log("sum:=>", sum);

sachinsuthariya
quelle