Como somar os valores de um objeto JavaScript?

86

Eu gostaria de somar os valores de um objeto.

Estou acostumado a fazer python onde seria apenas:

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

O código a seguir funciona, mas é muito código:

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)

Estou perdendo algo óbvio ou é assim que as coisas são?

Jonathan Vanasco
fonte

Respostas:

74

Você pode colocar tudo em uma função:

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


Para diversão, aqui está outra implementação usando Object.keys()e Array.reduce()(o suporte ao navegador não deve ser mais um grande 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)}`);

Mas isso parece ser bem mais lento: jsperf.com

Sirko
fonte
retorne sum + parseFloat (obj [chave] || 0) para verificar os valores falsey ou nulo / em branco
sumit
1
Excelente trabalho destacando a diferença de desempenho entre as soluções. Embora Object.keys().reducepareça muito mais elegante, é 60% mais lento.
Micnguyen
101

Pode ser tão simples quanto isso:

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

Citando MDN:

O Object.values()método retorna uma matriz de valores de propriedade enumeráveis ​​do próprio objeto, na mesma ordem fornecida por um for...inloop (a diferença é que um loop for-in enumera propriedades na cadeia de protótipo também).

de Object.values()em MDN

O reduce()método aplica uma função contra um acumulador e cada valor do array (da esquerda para a direita) para reduzi-lo a um único valor.

de Array.prototype.reduce()em MDN

Você pode usar essa função assim:

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

Observe que este código usa alguns recursos ECMAScript que não são suportados por alguns navegadores mais antigos (como o IE). Você pode precisar usar o Babel para compilar seu código.

Michał Perłakowski
fonte
3
Isso requer que você puxe uma biblioteca de 60K apenas para ter Object.values(), que será preenchida com um forloop em todos os navegadores além do Firefox. Mesmo sem um polyfill, é 4x mais lento do que um forloop normal para mim.
Blender
10
@Blender Você precisa usar o Babel de qualquer maneira se quiser usar qualquer um dos novos recursos do ECMAScript e ainda suportar navegadores mais antigos. Além disso, se alguém visitar esta questão, por exemplo, após 2 anos, os navegadores modernos provavelmente irão implementar Object.values()até esse momento.
Michał Perłakowski
A resposta aceita tem uma abordagem muito semelhante, mas a função passada reduceparece um pouco mais infalível. Você omitiu a análise de propósito?
Cerbrus de
@Cerbrus Presumi que todos os valores naquele objeto são números.
Michał Perłakowski de
12
@Blender Parece que eu estava certo - um ano e meio se passou e Object.values()é compatível com todos os navegadores modernos .
Michał Perłakowski
25

Se você estiver usando lodash, pode fazer algo como

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

Um forloop regular é bastante conciso:

var total = 0;

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

Você pode ter que adicionar object.hasOwnPropertyse você modificou o protótipo.

Liquidificador
fonte
14

Honestamente, devido aos nossos "tempos modernos", eu escolheria uma abordagem de programação funcional sempre que possível, assim:

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

Nosso acumulador acc, começando com um valor de 0, está acumulando todos os valores em loop de nosso objeto. Isso tem o benefício adicional de não depender de nenhuma variável interna ou externa; é uma função constante, então não será sobrescrito acidentalmente ... vença para ES2015!

Ricardo Magalhães
fonte
12

Alguma razão para você não estar usando apenas um for...inloop simples ?

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

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

http://jsfiddle.net/vZhXs/

Jbabey
fonte
11

Agora você pode usar a reducefunção e obter a soma.

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

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

PC Krishnadas
fonte
1

Estou um pouco atrasado para a festa, no entanto, se você precisa de uma solução mais robusta e flexível, então aqui está minha contribuição. Se você deseja somar apenas uma propriedade específica em uma combinação de objeto / matriz aninhada, bem como realizar outros métodos de agregação, aqui está uma pequena função que venho usando em um projeto 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;
}

É recursivo, não ES6, e deve funcionar na maioria dos navegadores semimodernos. Você o usa assim:

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

Repartição de parâmetros:

obj = um objeto ou uma
propriedade de matriz = a propriedade dentro dos objetos / matrizes aninhados que você deseja executar o método
agregado em agregação = o método agregado (soma, mínimo, máximo ou contagem)
raso = pode ser definido como verdadeiro / false ou um valor numérico
depth = deve ser deixado nulo ou indefinido (é usado para rastrear os retornos de chamada recursivos subsequentes)

Raso pode ser usado para melhorar o desempenho se você souber que não precisará pesquisar dados profundamente aninhados. Por exemplo, se você tivesse a seguinte matriz:

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

Se você quiser evitar o loop através da propriedade otherData, já que o valor que você vai agregar não está aninhado tão profundamente, você pode definir raso como verdadeiro.


fonte
1

Use 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
fonte
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
fonte
0

Encontrei essa solução com @jbabey ao tentar resolver um problema semelhante. Com uma pequena modificação, acertei. No meu caso, as chaves do objeto são números (489) e strings ("489"). Portanto, para resolver isso, cada chave é analisada. O seguinte código funciona:

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
fonte
0

Um ramda one liner:

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

export const sumValues = compose(sum, values);

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

Damian Green
fonte
0

Podemos interagir objeto usando a palavra-chave e pode executar qualquer operação aritmética.

// 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
fonte
0

Some o valor da chave do objeto por parse Integer. Converter o formato da string em inteiro e somar os valores

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

Surya R Praveen
fonte