2013-05-08 4 views
36

Я хотел бы суммировать значения объекта.Как суммировать значения объекта JavaScript?

Я привык к питону, где это было бы просто:

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) 

я упускаю что-то очевидное, или это так, как есть?

+0

Пожалуйста, подумайте об изменении принятого ответа на [мой ответ] (https://stackoverflow.com/a/39046365/3853934). –

ответ

39

Вы можете поместить все это в одной функции:

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


Ради FUN здесь еще одна реализация с использованием Object.keys() и Array.reduce() (поддержка браузера может быть проблема, хотя):

function sum(obj) { 
    return Object.keys(obj) 
       .reduce(function(sum, key){ 
       return sum + parseFloat(obj[key]); 
       }, 0); 
} 

Но это, кажется, путь медленнее: jsperf.com

+0

return sum + parseFloat (obj [key] || 0), чтобы проверить значения false или null/blank – sumit

12

Регулярное for цикл довольно кратким:

var total = 0; 

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

Вы, возможно, придется добавить в object.hasOwnProperty, если вы изменили прототип.

8

Любая причина, по которой вы не просто используете простой цикл 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/

11

Если вы используете lodash вы можете сделать что-то вроде

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

Честно говоря, учитывая наши «современные времена» Я бы с функциональным программированием подходом всякий раз, когда это возможно, например, так:

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

наш аккумулятор acc, начиная со значением 0, аккумулирует все петельные ценности нашего объекта. Это имеет дополнительное преимущество независимо от каких-либо внутренних или внешних переменных; это постоянная функция, поэтому она не будет случайно перезаписана ... выиграть для ES2015!

34

Это может быть так же просто, как:

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

Цитирование MDN:

Object.values() метод возвращает массив собственных значений перечислимых свойств заданного объекта, в том же порядке, что обеспечивается циклом for...in (разница заключается в том, что цикл for-in перечисляет свойства в цепочке прототипов).

от Object.values() on MDN

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

из Array.prototype.reduce() on MDN

Вы можете использовать эту функцию так:

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

Обратите внимание, что этот код использует некоторые функции ECMAScript, которые не поддерживаются некоторыми старыми браузерами (например, IE). Для компиляции кода вам может потребоваться использовать Babel.

+2

Это требует, чтобы вы вытащили библиотеку 60K * just *, чтобы иметь 'Object.values ​​()', который будет заполнен с помощью 'for' loop в каждом браузере, кроме Firefox. Даже без polyfill, это в 4 раза медленнее, чем обычный цикл 'for' для меня. – Blender

+5

@Blender В любом случае вам нужно использовать Babel, если вы хотите использовать * любые * из новых функций ECMAScript и по-прежнему поддерживать старые браузеры. Кроме того, если кто-то посещает этот вопрос, например, через 2 года, современные браузеры, вероятно, будут реализовывать 'Object.values ​​()' до этого времени. –

+0

Принятый ответ имеет очень похожий подход, но функция, переданная 'reduce', кажется немного более надежной. Вы специально не решили разбор? – Cerbrus

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); 
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 = либо объект, либо массив
свойство = свойство в пределах вложенных объектов/массивов вы хотите выполнить агрегат Способ по
агрегатного = совокупный метод (сумма, минимум, максимум, или считать)
мелкой = может либо быть установлен в True/False или числовое значение глубины
= должно быть оставлено пустым или неопределенным (используется для отслеживания последующих рекурсивных обратных вызовов)

Shallow могут быть использованы для повышения производительности, если вы знаете, что вы не потребуется искать глубоко вложенные данные. Например, если вы имели следующий массив:

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

Если вы хотите, чтобы избежать зацикливания через другие данные собственности, поскольку значение, которое вы собираетесь быть агрегирование не вложено, что глубоко, вы можете установить неглубоко истину.

Смежные вопросы