Як підсумувати значення об’єкта JavaScript?


86

Я хотів би підсумувати значення об’єкта.

Я звик до python там, де він просто був би:

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

Наступний код працює, але це багато коду:

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)

Чи мені не вистачає чогось очевидного, чи це просто так?

Відповіді:


74

Ви можете покласти все в одну функцію:

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


Для розваги ось ще одна реалізація з використанням Object.keys()і Array.reduce()(підтримка браузера вже не повинна бути великою проблемою):

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

Але це, здається, набагато повільніше: jsperf.com


повернути суму + parseFloat (obj [ключ] || 0) для перевірки фальшивих або нульових / порожніх значень
підсумок

1
Чудова робота, підкресливши різницю в продуктивності між рішеннями. Хоча Object.keys().reduceвигляд набагато елегантніший, він на 60% повільніший.
micnguyen

101

Це може бути так просто:

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

Цитування MDN:

Object.values()Метод повертає масив власних значень перелічуваних властивостей даного об'єкта, в тому ж порядку , як це передбачено в for...inциклі ( з тією різницею, що протягом циклу перебирає властивості в ланцюжку прототипів, а).

від Object.values()на MDN

reduce()Метод застосовує функцію від акумулятора і кожне значення масиву (зліва направо) , щоб зменшити його до одного значення.

від Array.prototype.reduce()на MDN

Ви можете використовувати цю функцію так:

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

Зверніть увагу, що цей код використовує деякі функції ECMAScript, які не підтримуються деякими старішими браузерами (наприклад, IE). Можливо, вам знадобиться використовувати Babel для компіляції коду.


3
Для цього необхідно потягнути бібліотеку 60K просто мати Object.values(), який буде polyfilled з forпетлею на будь-якому браузері , крім Firefox. Навіть без поліфілу це forдля мене в чотири рази повільніше, ніж звичайний цикл.
Blender

10
@Blender Вам все одно потрібно використовувати Babel, якщо ви хочете скористатися будь-якою з нових функцій ECMAScript і при цьому підтримувати старіші браузери. Крім того, якщо хтось відвідає це питання, наприклад, через 2 роки, сучасні браузери, ймовірно, впровадять Object.values()до цього часу.
Міхал Перлаковський,

Прийнята відповідь має дуже схожий підхід, але передана функція reduceздається трохи надійнішою. Ви навмисно залишили синтаксичний розбір?
Цербрус,

@Cerbrus Я припустив, що всі значення в цьому об'єкті є числами.
Міхал Перлаковський,

12
@Blender Здається, я мав рацію - минуло півтора року, і Object.values()його підтримують усі сучасні браузери .
Міхал Перлаковський

25

Якщо ви використовуєте лодаш, ви можете зробити щось на зразок

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

20

Регулярний forцикл досить стислий:

var total = 0;

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

Можливо, вам доведеться додати, object.hasOwnPropertyякщо ви модифікували прототип.


14

Чесно кажучи, враховуючи наш "сучасний час", я б по можливості використовував функціональний підхід до програмування, наприклад:

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

Наш накопичувач acc, починаючи зі значення 0, накопичує всі петельні значення нашого об'єкта. Це має додаткову перевагу - не залежить від будь-яких внутрішніх чи зовнішніх змінних; це постійна функція, тому вона не буде випадково перезаписана ... виграйте за ES2015!


12

Будь-яка причина, чому ви не просто використовуєте простий for...inцикл?

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

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

http://jsfiddle.net/vZhXs/



1

Я трохи запізнююсь до партії, однак, якщо вам потрібне більш надійне та гнучке рішення, то ось мій внесок. Якщо ви хочете підсумувати лише конкретну властивість у вкладеному комбінованому об’єкті / масиві, а також виконати інші агреговані методи, то ось невелика функція, яку я використовував у проекті 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;
}

Він рекурсивний, не ES6, і він повинен працювати в більшості напівсучасних браузерів. Ви використовуєте його так:

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

Розбивка параметрів:

obj = або
властивість об'єкта, або масиву = властивість усередині вкладених об'єктів / масивів, для яких ви хочете виконати метод
агрегату на agregate = метод агрегації (сума, мінімум, максимум або кількість)
shallow = може бути встановлено значення true / false або числове значення
глибина = слід залишити нулем або невизначеним (воно використовується для відстеження наступних рекурсивних зворотних викликів)

Неглибоку можна використовувати для підвищення продуктивності, якщо ви знаєте, що вам не потрібно буде шукати глибоко вкладені дані. Наприклад, якщо у вас був такий масив:

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

Якщо ви хочете уникнути перегляду властивості otherData, оскільки значення, яке ви збираєтеся агрегувати, не вкладене настільки глибоко, ви можете встановити для shallow значення true.




0

Я натрапив на це рішення від @jbabey, намагаючись вирішити подібну проблему. З невеликими змінами я це правильно зрозумів. У моєму випадку ключі об'єкта - це цифри (489) і рядки ("489"). Отже, щоб вирішити це, кожен ключ аналізується. Працює такий код:

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

Ramda один лайнер:

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

export const sumValues = compose(sum, values);

Використання: const summed = sumValues({ 'a': 1 , 'b': 2 , 'c':3 });


0

Ми можемо перебирати об'єкт , використовуючи в ключовому слові і можемо виконати будь-яку арифметичну операцію.

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

Підсумуйте значення ключа об’єкта за допомогою синтаксичного аналізу цілого числа. Перетворення формату рядка в ціле число та підсумовування значень

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.