Come sommare i valori di un oggetto JavaScript?


87

Vorrei sommare i valori di un oggetto.

Sono abituato a fare il pitone dove sarebbe solo:

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

Il codice seguente funziona, ma contiene molto codice:

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)

Mi manca qualcosa di ovvio o è solo così?

Risposte:


77

Potresti mettere tutto in una funzione:

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


Per divertimento, ecco un'altra implementazione che utilizza Object.keys()e Array.reduce()(il supporto del browser non dovrebbe più essere un grosso problema):

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

Ma questo sembra essere molto più lento: jsperf.com


restituire sum + parseFloat (obj [key] || 0) per controllare i valori falsey o null / blank
sommare il

1
Ottimo lavoro che evidenzia la differenza di prestazioni tra le soluzioni. Sebbene l' Object.keys().reduceaspetto sia molto più elegante, è del 60% più lento.
micnguyen

104

Può essere così semplice:

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

Citando MDN:

Il Object.values()metodo restituisce un array di valori di proprietà enumerabili di un dato oggetto, nello stesso ordine di quello fornito da un for...inciclo (con la differenza che un ciclo for-in enumera anche le proprietà nella catena del prototipo).

da Object.values()su MDN

Il reduce()metodo applica una funzione su un accumulatore e ogni valore dell'array (da sinistra a destra) per ridurlo a un singolo valore.

da Array.prototype.reduce()su MDN

Puoi usare questa funzione in questo modo:

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

Nota che questo codice utilizza alcune funzionalità ECMAScript che non sono supportate da alcuni browser meno recenti (come IE). Potrebbe essere necessario utilizzare Babel per compilare il codice.


3
Ciò richiede di estrarre una libreria da 60K solo per avere Object.values(), che verrà riempita con un forciclo su tutti i browser oltre a Firefox. Anche senza un polyfill, per me è 4 volte più lento di un normale forloop.
Blender

10
@Blender È comunque necessario utilizzare Babel se si desidera utilizzare una delle nuove funzionalità di ECMAScript e continuare a supportare i browser meno recenti. Inoltre, se qualcuno visita questa domanda ad esempio dopo 2 anni, i browser moderni probabilmente lo implementeranno Object.values()fino a quel momento.
Michał Perłakowski

La risposta accettata ha un approccio molto simile, ma la funzione passata reducesembra un po 'più infallibile. Hai lasciato fuori l'analisi di proposito?
Cerbrus

@Cerbrus Ho pensato che tutti i valori in quell'oggetto fossero numeri.
Michał Perłakowski

12
@Blender Sembra che avessi ragione - è passato un anno e mezzo ed Object.values()è supportato da tutti i browser moderni .
Michał Perłakowski

25

Se stai usando lodash puoi fare qualcosa di simile

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

20

Un forciclo regolare è piuttosto conciso:

var total = 0;

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

Potrebbe essere necessario aggiungere object.hasOwnPropertyse hai modificato il prototipo.


15

Onestamente, visti i nostri "tempi moderni", sceglierei un approccio di programmazione funzionale ogni volta che è possibile, in questo modo:

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

Il nostro accumulatore acc, che inizia con un valore di 0, sta accumulando tutti i valori in loop del nostro oggetto. Ciò ha l'ulteriore vantaggio di non dipendere da variabili interne o esterne; è una funzione costante quindi non verrà sovrascritta accidentalmente ... vinci per ES2015!


13

Ora puoi utilizzare la reducefunzione e ottenere la somma.

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

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


12

C'è qualche motivo per cui non stai usando solo un semplice for...inloop?

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

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

http://jsfiddle.net/vZhXs/


1

Sono un po 'tardivo alla festa, tuttavia, se hai bisogno di una soluzione più robusta e flessibile, ecco il mio contributo. Se vuoi sommare solo una proprietà specifica in una combinazione di oggetti / array annidati, oltre a eseguire altri metodi di aggregazione, ecco una piccola funzione che ho usato su un progetto React:

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

È ricorsivo, non ES6 e dovrebbe funzionare nella maggior parte dei browser semi-moderni. Lo usi in questo modo:

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

Ripartizione dei parametri:

obj = o un oggetto o una
proprietà array = la proprietà all'interno degli oggetti / array nidificati che desideri eseguire il metodo
aggregato su aggregate = il metodo aggregato (sum, min, max o count)
shallow = può essere impostato su true / false o un valore numerico
depth = deve essere lasciato nullo o non definito (viene utilizzato per tracciare i successivi callback ricorsivi)

Shallow può essere utilizzato per migliorare le prestazioni se sai che non avrai bisogno di cercare dati nidificati in profondità. Ad esempio, se avessi il seguente array:

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

Se si desidera evitare di scorrere la proprietà otherData poiché il valore che si aggregherà non è annidato così profondamente, è possibile impostare shallow su true.


1

Usa 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


1

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

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

alert(sum)


0

Mi sono imbattuto in questa soluzione da @jbabey mentre cercavo di risolvere un problema simile. Con una piccola modifica, ho capito bene. Nel mio caso, le chiavi dell'oggetto sono numeri (489) e stringhe ("489"). Quindi per risolvere questo problema, ogni chiave viene analizzata. Il codice seguente funziona:

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

0

Una linea ramda one:

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

export const sumValues = compose(sum, values);

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


0

Siamo in grado di iterare oggetto utilizzando in parole chiave e in grado di eseguire qualsiasi operazione aritmetica.

// 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);


0

Sommare il valore della chiave dell'oggetto analizzando Integer. Conversione del formato stringa in numero intero e somma dei valori

var obj = {
  pay: 22
};
obj.pay;
console.log(obj.pay);
var x = parseInt(obj.pay);
console.log(x + 20);

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.