¿Cómo sumar los valores de un objeto JavaScript?

86

Me gustaría sumar los valores de un objeto.

Estoy acostumbrado a Python donde solo estaría:

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

El siguiente código funciona, pero es mucho 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)

¿Me estoy perdiendo algo obvio o es así?

Jonathan Vanasco
fuente

Respuestas:

74

Podrías ponerlo todo en una función:

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


Por diversión, aquí hay otra implementación que usa Object.keys()y Array.reduce()(el soporte del navegador ya no debería ser un gran 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)}`);

Pero esto parece ser mucho más lento: jsperf.com

Sirko
fuente
return sum + parseFloat (obj [key] || 0) para verificar los valores falsey o null / blank
sumit
1
Gran trabajo destacando la diferencia de rendimiento entre las soluciones. Si bien el Object.keys().reduceaspecto es mucho más elegante, es un 60% más lento.
micnguyen
101

Puede ser tan simple como eso:

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

Citando MDN:

El Object.values()método devuelve una matriz de valores de propiedad enumerables propios de un objeto dado, en el mismo orden que el proporcionado por un for...inbucle (la diferencia es que un bucle for-in también enumera las propiedades en la cadena del prototipo).

desde Object.values()MDN

El reduce()método aplica una función contra un acumulador y cada valor de la matriz (de izquierda a derecha) para reducirlo a un solo valor.

desde Array.prototype.reduce()MDN

Puedes usar esta función así:

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

Tenga en cuenta que este código utiliza algunas funciones de ECMAScript que no son compatibles con algunos navegadores más antiguos (como IE). Es posible que deba usar Babel para compilar su código.

Michał Perłakowski
fuente
3
Esto requiere que extraiga una biblioteca de 60K solo para tener Object.values(), que se rellenará con un forbucle en todos los navegadores, además de Firefox. Incluso sin un polyfill, es 4 veces más lento que un forbucle normal para mí.
Blender
10
@Blender Necesita usar Babel de todos modos si desea usar cualquiera de las nuevas funciones de ECMAScript y aún es compatible con navegadores más antiguos. Además, si alguien visita esta pregunta, por ejemplo, después de 2 años, los navegadores modernos probablemente lo implementarán Object.values()hasta ese momento.
Michał Perłakowski
La respuesta aceptada tiene un enfoque muy similar, pero la función pasada reduceparece un poco más infalible. ¿Dejó de lado el análisis a propósito?
Cerbrus
@Cerbrus Supuse que todos los valores en ese objeto son números.
Michał Perłakowski
12
@Blender Parece que tenía razón: pasó un año y medio y Object.values()es compatible con todos los navegadores modernos .
Michał Perłakowski
25

Si está usando lodash, puede hacer algo como

_.sum(_.values({ 'a': 1 , 'b': 2 , 'c':3 })) 
León
fuente
20

Un forbucle regular es bastante conciso:

var total = 0;

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

Es posible que deba agregarlo object.hasOwnPropertysi modificó el prototipo.

Licuadora
fuente
14

Honestamente, dados nuestros "tiempos modernos", optaría por un enfoque de programación funcional siempre que sea posible, así:

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

Nuestro acumulador acc, comenzando con un valor de 0, está acumulando todos los valores en bucle de nuestro objeto. Esto tiene el beneficio adicional de no depender de ninguna variable interna o externa; es una función constante, por lo que no se sobrescribirá accidentalmente ... ¡gana para ES2015!

Ricardo Magalhães
fuente
12

¿Alguna razón por la que no estás usando un simple for...inbucle?

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

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

http://jsfiddle.net/vZhXs/

jbabey
fuente
11

Ahora puede hacer uso de la reducefunción y obtener la suma.

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

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

Krishnadas PC
fuente
1

Llego un poco tarde a la fiesta, sin embargo, si necesita una solución más robusta y flexible, aquí está mi contribución. Si desea sumar solo una propiedad específica en un combo de objeto / matriz anidado, así como realizar otros métodos agregados, aquí hay una pequeña función que he estado usando en un proyecto de 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;
}

Es recursivo, no es ES6 y debería funcionar en la mayoría de los navegadores semi-modernos. Lo usas así:

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

Desglose de parámetros:

obj =
propiedad de un objeto o de una matriz = la propiedad dentro de los objetos / matrices anidados que desea realizar el método
agregado en agregado = el método agregado (suma, mínimo, máximo o recuento)
superficial = se puede establecer en verdadero / falso o un valor numérico
profundidad = debe dejarse nulo o indefinido (se usa para rastrear las devoluciones de llamada recursivas posteriores)

Shallow se puede utilizar para mejorar el rendimiento si sabe que no necesitará buscar datos profundamente anidados. Por ejemplo, si tuviera la siguiente matriz:

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

Si desea evitar recorrer la propiedad otherData ya que el valor que va a agregar no está anidado tan profundamente, puede establecer shallow en verdadero.


fuente
1

Usar 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
fuente
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
fuente
0

Encontré esta solución de @jbabey mientras intentaba resolver un problema similar. Con una pequeña modificación, lo hice bien. En mi caso, las claves de objeto son números (489) y cadenas ("489"). Por lo tanto, para resolver esto, cada clave se analiza. El siguiente 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
fuente
0

Un forro ramda one:

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

export const sumValues = compose(sum, values);

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

Damian Green
fuente
0

Podemos repetir objeto utilizando en la palabra clave y puede realizar cualquier operación 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
fuente
0

Sume el valor de la clave del objeto analizando Integer. Conversión de formato de cadena a entero y suma de valores

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

Surya R Praveen
fuente