Làm cách nào để tính tổng các giá trị của một đối tượng JavaScript?


86

Tôi muốn tính tổng các giá trị của một đối tượng.

Tôi đã quen với python, nơi nó sẽ chỉ là:

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

Đoạn mã sau hoạt động, nhưng đó là rất nhiều mã:

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)

Tôi có thiếu bất cứ điều gì rõ ràng không, hay chỉ là như vậy?

Câu trả lời:


74

Bạn có thể đặt tất cả trong một chức năng:

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


Vì lợi ích của nó, đây là một triển khai khác bằng cách sử dụng Object.keys()Array.reduce()(hỗ trợ trình duyệt không còn là vấn đề lớn nữa):

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

Nhưng điều này có vẻ chậm hơn: jsperf.com


return sum + parseFloat (obj [key] || 0) để kiểm tra giá trị falsey hoặc null / blank
sumit

1
Công việc tuyệt vời làm nổi bật sự khác biệt về hiệu suất giữa các giải pháp. Trong khi Object.keys().reducetrông thanh lịch hơn rất nhiều, nó lại chậm hơn 60%.
micnguyen

101

Nó có thể đơn giản như vậy:

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

Trích dẫn MDN:

Các Object.values()phương thức trả về một mảng các giá trị tài sản đếm được riêng một đối tượng nhất định, trong thứ tự như được cung cấp bởi một for...invòng lặp (sự khác biệt là có một cho-in liệt kê vòng lặp tính trong chuỗi ban đầu cũng).

từ Object.values()MDN

Các reduce()phương pháp áp dụng một chức năng chống lại một ắc và mỗi giá trị của mảng (từ phải từ trái sang) để giảm nó vào một giá trị duy nhất.

từ Array.prototype.reduce()MDN

Bạn có thể sử dụng chức năng này như sau:

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

Lưu ý rằng mã này sử dụng một số tính năng ECMAScript không được hỗ trợ bởi một số trình duyệt cũ hơn (như IE). Bạn có thể cần sử dụng Babel để biên dịch mã của mình.


3
Điều này yêu cầu bạn kéo một thư viện 60K chỉ để có Object.values(), thư viện này sẽ được lấp đầy với một forvòng lặp trên mọi trình duyệt ngoài Firefox. Ngay cả khi không có polyfill, nó chậm hơn 4 lần so với forvòng lặp thông thường đối với tôi.
Máy xay sinh tố

10
@Blender Bạn vẫn cần sử dụng Babel nếu muốn sử dụng bất kỳ tính năng ECMAScript mới nào và vẫn hỗ trợ các trình duyệt cũ hơn. Bên cạnh đó, nếu ai đó truy cập câu hỏi này chẳng hạn sau 2 năm, các trình duyệt hiện đại có thể sẽ thực hiện Object.values()cho đến thời điểm đó.
Michał Perłakowski

Câu trả lời được chấp nhận có cách tiếp cận rất giống, nhưng hàm được chuyển tới reducecó vẻ dễ hiểu hơn một chút. Bạn có cố ý bỏ qua phân tích cú pháp không?
Cerbrus

@Cerbrus Tôi đã giả định rằng tất cả các giá trị trong đối tượng đó là số.
Michał Perłakowski

12
@Blender Có vẻ như tôi đã đúng - một năm rưỡi đã trôi qua và Object.values()được hỗ trợ bởi tất cả các trình duyệt hiện đại .
Michał Perłakowski

25

Nếu bạn đang sử dụng lodash, bạn có thể làm điều gì đó như

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

20

Một forvòng lặp thông thường khá ngắn gọn:

var total = 0;

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

Bạn có thể phải thêm vào object.hasOwnPropertynếu bạn sửa đổi nguyên mẫu.


14

Thành thật mà nói, với "thời hiện đại" của chúng ta, tôi sẽ sử dụng phương pháp lập trình hàm bất cứ khi nào có thể, như vậy:

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

Bộ tích lũy của chúng tôi acc, bắt đầu bằng giá trị 0, đang tích lũy tất cả các giá trị lặp lại của đối tượng của chúng tôi. Điều này có thêm lợi ích là không phụ thuộc vào bất kỳ biến bên trong hoặc bên ngoài nào; đó là một chức năng không đổi nên nó sẽ không vô tình bị ghi đè ... win cho ES2015!


12

Bất kỳ lý do gì bạn không chỉ sử dụng một for...invòng lặp đơn giản ?

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

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

http://jsfiddle.net/vZhXs/


11

Bây giờ bạn có thể sử dụng reducehàm và lấy tổng.

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

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


1

Tuy nhiên, tôi hơi muộn đối với bữa tiệc, nếu bạn yêu cầu một giải pháp linh hoạt và mạnh mẽ hơn thì đây là đóng góp của tôi. Nếu bạn chỉ muốn tính tổng một thuộc tính cụ thể trong tổ hợp đối tượng / mảng lồng nhau, cũng như thực hiện các phương thức tổng hợp khác, thì đây là một hàm nhỏ mà tôi đã sử dụng trên một dự án 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;
}

Nó là đệ quy, không phải ES6, và nó sẽ hoạt động trong hầu hết các trình duyệt bán hiện đại. Bạn sử dụng nó như thế này:

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

Phân tích thông số:

obj = hoặc một đối tượng hoặc một mảng
bất động sản = tài sản trong phạm vi các đối tượng lồng nhau / mảng bạn muốn thực hiện các phương pháp tổng hợp về
tổng hợp = phương pháp tổng hợp (Tóm lại, min, max, hoặc đếm)
cạn = hoặc có thể được thiết lập để / đúng false hoặc giá trị số
chiều sâu = phải được để trống hoặc không xác định (nó được sử dụng để theo dõi các lệnh gọi lại đệ quy tiếp theo)

Có thể sử dụng Shallow để nâng cao hiệu suất nếu bạn biết rằng bạn sẽ không cần phải tìm kiếm dữ liệu lồng ghép sâu. Ví dụ: nếu bạn có mảng sau:

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

Nếu bạn muốn tránh lặp qua thuộc tính OtherData vì giá trị bạn sắp tổng hợp không được lồng sâu như vậy, bạn có thể đặt nông thành true.




0

Tôi đã tìm thấy giải pháp này từ @jbabey trong khi cố gắng giải quyết một vấn đề tương tự. Với một chút sửa đổi, tôi đã làm đúng. Trong trường hợp của tôi, các khóa đối tượng là số (489) và chuỗi ("489"). Do đó, để giải quyết vấn đề này, mỗi khóa được phân tích cú pháp. Đoạn mã sau hoạt động:

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

Một ramda một lớp lót:

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

export const sumValues = compose(sum, values);

Sử dụng: const summed = sumValues({ 'a': 1 , 'b': 2 , 'c':3 });


0

Chúng ta có thể lặp đối tượng sử dụng trong từ khóa và có thể thực hiện bất kỳ hoạt động số học.

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

Tính tổng giá trị khóa đối tượng bằng cách phân tích cú pháp Số nguyên. Chuyển đổi định dạng chuỗi thành số nguyên và tính tổng các giá trị

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.