Как суммировать значения объекта 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)

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

Джонатан Ванаско
источник

Ответы:

75

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

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

Сирко
источник
return sum + parseFloat (obj [key] || 0) для проверки ложных или нулевых / пустых значений
sumit
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для меня это в 4 раза медленнее, чем обычный цикл.
Blender
10
@Blender Вам все равно нужно использовать Babel, если вы хотите использовать какие-либо новые функции ECMAScript и по-прежнему поддерживать старые браузеры. Кроме того, если кто-то посетит этот вопрос, например, через 2 года, современные браузеры, вероятно, будут реализованы Object.values()до этого времени.
Michał Perłakowski
Принятый ответ имеет очень похожий подход, но переданная функция reduceкажется немного более надежной. Вы специально пропустили парсинг?
Cerbrus
@Cerbrus Я предположил, что все значения в этом объекте - числа.
Michał Perłakowski
12
@Blender Вроде я был прав - прошло полтора года, и Object.values()поддерживается всеми современными браузерами .
Michał Perłakowski
25

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

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

jbabey
источник
11

Теперь вы можете воспользоваться reduceфункцией и получить сумму.

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

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

Кришнадас ПК
источник
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 = либо объект, либо
свойство массива = свойство внутри вложенных объектов / массивов, которое вы хотите выполнить агрегатным методом для
агрегата = агрегатный метод (сумма, мин., макс. или счетчик)
shallow = может иметь значение true / false или числовое значение
depth = следует оставить null или undefined (используется для отслеживания последующих рекурсивных обратных вызовов)

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

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

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


источник
1

Используйте 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

Исмаэль Сощински
источник
1

let prices = {
  "apple": 100,
  "banana": 300,
  "orange": 250
};

let sum = 0;
for (let price of Object.values(prices)) {
  sum += price;
}

alert(sum)

Судам Диссанаяке
источник
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

Мы можем перебирать объект, используя ключевое слово in, и можем выполнять любую арифметическую операцию.

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

Суммируйте значение ключа объекта путем синтаксического анализа Integer. Преобразование строкового формата в целое число и суммирование значений

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

Сурья Р. Правин
источник