it-swarm.com.ru

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

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

Я привык к 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)

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

51
Jonathan Vanasco

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

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

52
Sirko

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

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

Цитируя MDN:

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

с Object.values() на MDN

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

с Array.prototype.reduce() на MDN

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

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

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

57
Michał Perłakowski

Обычный цикл for довольно лаконичен:

var total = 0;

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

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

17
Blender

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

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

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

10
jbabey

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

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

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

8
Ricardo Magalhães

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

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

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

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

1
user5487386

A ramda один вкладыш:

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

export const sumValues = compose(sum, values);

Использование: const summed = sumValues({ 'a': 1 , 'b': 2 , 'c':3 });

0
Damian Green

Я столкнулся с этим решением от @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
Olu Adabonyan