Jak zsumować wartości obiektu JavaScript?

86

Chciałbym zsumować wartości przedmiotu.

Jestem przyzwyczajony do Pythona, gdzie byłoby po prostu:

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

Poniższy kod działa, ale jest go dużo:

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)

Czy brakuje mi czegoś oczywistego, czy po prostu tak jest?

Jonathan Vanasco
źródło

Odpowiedzi:

75

Możesz umieścić to wszystko w jednej funkcji:

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


Dla zabawy oto kolejna implementacja wykorzystująca Object.keys()i Array.reduce()(obsługa przeglądarek nie powinna już być dużym problemem):

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

Ale to wydaje się być znacznie wolniejsze: jsperf.com

Sirko
źródło
return sum + parseFloat (obj [key] || 0), aby sprawdzić wartości falsey lub null / blank
sumit
1
Świetna robota, podkreślając różnicę w wydajności między rozwiązaniami. Chociaż Object.keys().reducewygląda o wiele bardziej elegancko, jest o 60% wolniejszy.
micnguyen
101

To może być takie proste:

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

Cytując MDN:

Object.values()Zwraca tablicę wartości własnych przeliczalnych własności danego obiektu, w takiej samej kolejności jak te dostarczane przez for...inpętlę (przy różnicą, że do pętli w wylicza właściwości łańcucha prototypu również).

z Object.values()MDN

reduce()Sposób stosuje funkcje przed akumulatorem i każdą wartość tablicy (od lewej do prawej) w celu zredukowania go do jednej wartości.

z Array.prototype.reduce()MDN

Możesz użyć tej funkcji w ten sposób:

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

Zauważ, że ten kod wykorzystuje niektóre funkcje ECMAScript, które nie są obsługiwane przez niektóre starsze przeglądarki (takie jak IE). Do skompilowania kodu może być konieczne użycie Babel .

Michał Perłakowski
źródło
3
Wymaga to można wyciągnąć bibliotekę 60K prostu mieć Object.values(), która zostanie polyfilled z forpętli na każdej przeglądarce oprócz Firefoksa. Nawet bez polyfillu jest fordla mnie 4x wolniejszy niż zwykła pętla.
Blender
10
@Blender Mimo wszystko musisz użyć Babel, jeśli chcesz korzystać z którejkolwiek z nowych funkcji ECMAScript i nadal obsługiwać starsze przeglądarki. Zresztą jeśli ktoś odwiedzi to pytanie np. Po 2 latach, to nowoczesne przeglądarki zapewne Object.values()do tego czasu będą się implementować .
Michał Perłakowski
Przyjęta odpowiedź ma bardzo podobne podejście, ale funkcja przekazana do reducewydaje się nieco bardziej niezawodna. Czy celowo pominąłeś analizę?
Cerbrus
@Cerbrus Założyłem, że wszystkie wartości w tym obiekcie są liczbami.
Michał Perłakowski
12
@Blender Wygląda na to, że miałem rację - minęło półtora roku i Object.values()jest obsługiwany przez wszystkie nowoczesne przeglądarki .
Michał Perłakowski
25

Jeśli używasz lodash, możesz zrobić coś takiego

_.sum(_.values({ 'a': 1 , 'b': 2 , 'c':3 })) 
Leon
źródło
20

Regularna forpętla jest dość zwięzła:

var total = 0;

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

Może być konieczne dodanie, object.hasOwnPropertyjeśli zmodyfikowałeś prototyp.

Mikser
źródło
14

Szczerze mówiąc, biorąc pod uwagę nasze „współczesne czasy”, w miarę możliwości wybrałbym podejście programowania funkcjonalnego, na przykład:

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

Nasz akumulator acc, zaczynając od wartości 0, gromadzi wszystkie zapętlone wartości naszego obiektu. Ma to dodatkową zaletę, że nie jest zależny od żadnych zmiennych wewnętrznych ani zewnętrznych; to stała funkcja, więc nie zostanie przypadkowo nadpisana ... wygraj w ES2015!

Ricardo Magalhães
źródło
12

Czy jest jakiś powód, dla którego nie używasz zwykłej for...inpętli?

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

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

http://jsfiddle.net/vZhXs/

jbabey
źródło
11

Teraz możesz skorzystać z reducefunkcji i otrzymać sumę.

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

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

Krishnadas PC
źródło
1

Jestem trochę spóźniony na imprezę, jednak jeśli potrzebujesz solidniejszego i bardziej elastycznego rozwiązania, oto mój wkład. Jeśli chcesz zsumować tylko określoną właściwość w zagnieżdżonej kombinacji obiekt / tablica, a także wykonać inne metody agregacji, oto mała funkcja, której używałem w projekcie 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;
}

Jest rekurencyjny, inny niż ES6 i powinien działać w większości pół-nowoczesnych przeglądarek. Używasz tego w ten sposób:

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

Podział parametrów:

obj = obiekt lub
właściwość tablicy = właściwość w zagnieżdżonych obiektach / tablicach, na których chcesz wykonać metodę
agregacji na agregate = metoda agregacji (suma, min, maksimum lub liczba)
płytka = może być ustawiona na true / false lub wartość liczbową
depth = należy pozostawić pustą lub niezdefiniowaną (służy do śledzenia kolejnych rekurencyjnych wywołań zwrotnych)

Shallow może służyć do zwiększenia wydajności, jeśli wiesz, że nie będziesz musiał przeszukiwać głęboko zagnieżdżonych danych. Na przykład, jeśli masz następującą tablicę:

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

Jeśli chcesz uniknąć zapętlania się przez właściwość otherData, ponieważ wartość, którą zamierzasz agregować, nie jest zagnieżdżona tak głęboko, możesz ustawić wartość shallow na true.


źródło
1

Użyj 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
źródło
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
źródło
0

Natknąłem się na to rozwiązanie od @jbabey, próbując rozwiązać podobny problem. Po niewielkiej modyfikacji udało mi się to dobrze. W moim przypadku klucze obiektów to liczby (489) i łańcuchy („489”). Dlatego aby rozwiązać ten problem, każdy klucz jest analizowany. Poniższy kod działa:

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
źródło
0

Ramda jedna wkładka:

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

export const sumValues = compose(sum, values);

Posługiwać się: const summed = sumValues({ 'a': 1 , 'b': 2 , 'c':3 });

Damian Green
źródło
0

Możemy iteracyjne obiektu za pomocą w kluczowych i może wykonać dowolną operację arytmetyczną.

// 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
źródło
0

Zsumuj wartość klucza obiektu przez analizę liczby całkowitej. Konwersja formatu string na liczbę całkowitą i sumowanie wartości

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

Surya R Praveen
źródło