วิธีการรวมค่าของวัตถุ JavaScript?


87

ฉันต้องการรวมค่าของวัตถุ

ฉันคุ้นเคยกับ 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)

ฉันพลาดอะไรที่ชัดเจนหรือเป็นแบบที่เป็นอยู่?

คำตอบ:


77

คุณสามารถใส่ทั้งหมดไว้ในฟังก์ชันเดียว:

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


ส่งคืน sum + parseFloat (obj [key] || 0) เพื่อตรวจสอบค่า falsey หรือ null / blank
sumit

1
งานที่ยอดเยี่ยมเน้นความแตกต่างด้านประสิทธิภาพระหว่างโซลูชัน แม้ว่าObject.keys().reduceรูปลักษณ์จะดูหรูหราขึ้นมาก แต่ก็ช้าลง 60%
micnguyen

104

สามารถทำได้ง่าย ๆ ดังนี้:

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 คุณจำเป็นต้องใช้บาเบลอยู่แล้วถ้าคุณต้องการที่จะใช้ใด ๆของ ECMAScript ใหม่คุณสมบัติและยังคงสนับสนุนเบราว์เซอร์รุ่นเก่า นอกจากนี้หากมีคนเข้ามาถามคำถามนี้หลังจากผ่านไป 2 ปีเบราว์เซอร์สมัยใหม่อาจใช้งานได้Object.values()จนถึงเวลานั้น
MichałPerłakowski

คำตอบที่ยอมรับมีแนวทางที่คล้ายกันมาก แต่ฟังก์ชั่นที่ส่งผ่านreduceดูเหมือนจะเข้าใจผิดได้มากกว่าเล็กน้อย คุณละทิ้งการแยกวิเคราะห์โดยเจตนาหรือไม่?
Cerbrus

@Cerbrus ฉันคิดว่าค่าทั้งหมดในวัตถุนั้นเป็นตัวเลข
MichałPerłakowski

12
@Blender ดูเหมือนว่าฉันถูกขวา - หนึ่งปีครึ่งผ่านไปและได้รับการสนับสนุนจากเบราว์เซอร์ที่ทันสมัยObject.values()
MichałPerłakowski


20

forวงปกติค่อนข้างรัดกุม:

var total = 0;

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

คุณอาจต้องเพิ่มobject.hasOwnPropertyหากคุณแก้ไขต้นแบบ


15

ด้วยความสัตย์จริงจาก "ยุคปัจจุบัน" ของเราฉันจะใช้แนวทางการเขียนโปรแกรมเชิงฟังก์ชันเมื่อใดก็ตามที่ทำได้ดังนี้:

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

ตัวสะสมของเราaccเริ่มต้นด้วยค่า 0 กำลังสะสมค่าที่วนซ้ำทั้งหมดของวัตถุของเรา สิ่งนี้มีประโยชน์เพิ่มเติมโดยไม่ขึ้นอยู่กับตัวแปรภายในหรือภายนอกใด ๆ มันเป็นฟังก์ชันคงที่ดังนั้นจะไม่ถูกเขียนทับโดยบังเอิญ ... ชนะ ES2015!




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 = ทั้งอ็อบเจ็กต์หรือ
คุณสมบัติอาร์เรย์= คุณสมบัติภายในอ็อบเจ็กต์ / อาร์เรย์ที่ซ้อนกันคุณต้องการใช้เมธอดการ
รวมบนการรวม = วิธีการรวม (sum, min, max หรือ count)
ตื้น = สามารถตั้งค่าเป็น true / เท็จหรือ
ความลึกของค่าตัวเลข= ควรปล่อยให้ว่างหรือไม่ได้กำหนด (ใช้เพื่อติดตามการเรียกกลับแบบเรียกซ้ำในภายหลัง)

ตื้นสามารถใช้เพื่อเพิ่มประสิทธิภาพได้หากคุณรู้ว่าไม่จำเป็นต้องค้นหาข้อมูลที่ซ้อนกันลึก ๆ ตัวอย่างเช่นถ้าคุณมีอาร์เรย์ต่อไปนี้:

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

หากคุณต้องการหลีกเลี่ยงการวนลูปผ่านคุณสมบัติ otherData เนื่องจากค่าที่คุณกำลังจะรวมนั้นไม่ได้ซ้อนกันลึกขนาดนั้นคุณสามารถตั้งค่าตื้นเป็นจริงได้




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

เราสามารถย้ำวัตถุที่ใช้ในคำหลักและสามารถดำเนินการใด ๆ ทางคณิตศาสตร์

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.