Javascript ลด () กับ Object


182

มีวิธี Array ที่ดีในการreduce()รับค่าหนึ่งค่าจาก Array ตัวอย่าง:

[0,1,2,3,4].reduce(function(previousValue, currentValue, index, array){
  return previousValue + currentValue;
});

วิธีที่ดีที่สุดในการบรรลุวัตถุเดียวกันคืออะไร? ฉันต้องการทำสิ่งนี้:

{ 
    a: {value:1}, 
    b: {value:2}, 
    c: {value:3} 
}.reduce(function(previous, current, index, array){
  return previous.value + current.value;
});

อย่างไรก็ตามวัตถุดูเหมือนจะไม่ได้มีreduce()การใช้วิธีการใด ๆ


1
คุณกำลังใช้Underscore.js?
Sethen

Nope ขีดล่างให้การลดลงสำหรับวัตถุหรือไม่
Pavel S.

ฉันจำไม่ได้ ฉันรู้ว่ามันมีreduceวิธี ฉันจะตรวจสอบที่นั่น แม้ว่าการแก้ปัญหาดูเหมือนจะไม่ยาก
Sethen

1
@Sethen Maleno, @Pavel: ใช่_มีการลดสำหรับวัตถุ ไม่แน่ใจว่ามันทำงานโดยบังเอิญหรือการสนับสนุนวัตถุมีเจตนา แต่แน่นอนคุณสามารถส่งวัตถุได้ในตัวอย่างของคำถามนี้และมันจะ (ตามแนวคิด) for..inเรียกฟังก์ชันตัววนซ้ำของคุณด้วยค่าที่พบในแต่ละคีย์
Roatin Marth

คำตอบ:


55

Object.valuesสิ่งที่คุณต้องการจริงในกรณีนี้คือ นี่คือการปรับใช้ES6 ที่กระชับโดยคำนึงถึง:

const add = {
  a: {value:1},
  b: {value:2},
  c: {value:3}
}

const total = Object.values(add).reduce((t, {value}) => t + value, 0)

console.log(total) // 6

หรือเพียงแค่:

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

const total = Object.values(add).reduce((t, n) => t + n)

console.log(total) // 6

นั่นคือ Array.prototype.values ​​() ที่คุณเชื่อมโยงกับ - แก้ไขแล้ว
Jonathan Wood

281

ทางเลือกหนึ่งreduceคือkeys():

var o = { 
    a: {value:1}, 
    b: {value:2}, 
    c: {value:3} 
};

Object.keys(o).reduce(function (previous, key) {
    return previous + o[key].value;
}, 0);

ด้วยวิธีนี้คุณจะต้องการที่จะระบุค่าเริ่มต้นหรือรอบที่ 1 'a' + 2จะได้รับ

หากคุณต้องการผลลัพธ์เป็นวัตถุ ( { value: ... }) คุณจะต้องเริ่มต้นและส่งคืนวัตถุทุกครั้ง:

Object.keys(o).reduce(function (previous, key) {
    previous.value += o[key].value;
    return previous;
}, { value: 0 });

15
คำตอบที่ดี แต่มันสามารถอ่านได้มากกว่าที่จะใช้ Object.values ​​แทน Object.keys เพราะเรามีความกังวลเกี่ยวกับค่าที่นี่ไม่ใช่กุญแจ ควรเป็นดังนี้: Object.values ​​(o) .reduce ((total, current) => total + current.value, 0);
Mina Luke

3
Object.values ​​มีการสนับสนุนเบราว์เซอร์ที่เลวร้ายยิ่งกว่า Object.keys แต่นั่นอาจไม่เป็นปัญหาหากคุณใช้ polyfill หรือคุณส่งข้อมูลกับ Babel
duhaime

แน่นอนว่าฉันใช้สิ่งนี้ในคีย์ที่ได้รับบีทรูทจากโมเดล Mongo และฉันผ่านวัตถุเปล่าเป็นค่าเริ่มต้นไปสู่ผลลัพธ์ของการลดจำนวนคีย์และเท่าที่ฉันใช้@babel/plugin-proposal-object-rest-spreadปลั๊กอินฉันก็กระจายค่าแอคคูเลเตอร์ใน การกลับมาของการลดและมันทำงานได้อย่างมีเสน่ห์ แต่ฉันก็น่าประหลาดใจถ้าฉันทำอะไรผิดพลาดเพราะฉันผ่าน obejct ไปที่ค่าเริ่มต้นของการลดและคำตอบของคุณพิสูจน์ฉันว่าฉันทำสิ่งที่ถูกต้อง!
a_m_dev

55

การใช้งาน ES6: Object.entries ()

const o = {
  a: {value: 1},
  b: {value: 2},
  c: {value: 3}
};

const total = Object.entries(o).reduce(function (total, pair) {
  const [key, value] = pair;
  return total + value;
}, 0);

3
Object.entries(o); // returns [['value',1],['value',2],['value',3]]
faboulaws

1
const [สำคัญค่า] = คู่; ฉันไม่เคยเห็นสิ่งนี้!
Martin Meeser

9
@ martin-meeser - สิ่งนี้เรียกว่าการทำลายล้าง เราสามารถเว้นบรรทัดนี้ได้โดยเปลี่ยนfunction (total, pair)เป็นfunction (total, [key, value])
Jakub Zawiślak

ในขณะที่entriesเป็นวิธีที่สะอาดกว่าในการทำเช่นนี้keysแต่มันมีประสิทธิภาพน้อยกว่า hackernoon.com/…
robdonn

@ faboulaws Object.entries (o); // ส่งคืน [["a", {value: 1}], ["b", {value: 2}], ["c", {value: 3}]]
โทมัส

19

ก่อนอื่นคุณคงไม่ได้สิ่งที่คุณค่าก่อนหน้าลดลง

ในรหัสหลอกคุณมีreturn previous.value + current.valueดังนั้นpreviousค่าจะเป็นตัวเลขในการโทรครั้งต่อไปไม่ใช่วัตถุ

ประการที่สองreduceเป็นวิธี Array ไม่ใช่วัตถุและคุณไม่สามารถพึ่งพาลำดับเมื่อคุณทำซ้ำคุณสมบัติของวัตถุ (ดู: https://developer.mozilla.org/en-US/docs/) JavaScript / การอ้างอิง / ข้อความ / สำหรับ ... ใน , สิ่งนี้ถูกนำไปใช้กับObject.keysด้วย); ดังนั้นฉันจึงไม่แน่ใจว่าการใช้reduceกับวัตถุนั้นสมเหตุสมผลหรือไม่

อย่างไรก็ตามหากการสั่งซื้อนั้นไม่สำคัญคุณสามารถ:

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

หรือคุณสามารถแมปค่าของวัตถุ:

Object.keys(obj).map(function(key) { return this[key].value }, obj).reduce(function (previous, current) {
    return previous + current;
});

PS ใน ES6 พร้อมกับฟังก์ชั่นไวยากรณ์ของลูกศรไขมัน (มีอยู่แล้วใน Firefox Nightly) คุณสามารถลดขนาดได้บ้าง:

Object.keys(obj).map(key => obj[key].value).reduce((previous, current) => previous + current);

3

1:

[{value:5}, {value:10}].reduce((previousValue, currentValue) => { return {value: previousValue.value + currentValue.value}})

>> Object {value: 15}

2:

[{value:5}, {value:10}].map(item => item.value).reduce((previousValue, currentValue) => {return previousValue + currentValue })

>> 15

3:

[{value:5}, {value:10}].reduce(function (previousValue, currentValue) {
      return {value: previousValue.value + currentValue.value};
})

>> Object {value: 15}

2

ขยาย Object.prototype

Object.prototype.reduce = function( reduceCallback, initialValue ) {
    var obj = this, keys = Object.keys( obj );

    return keys.reduce( function( prevVal, item, idx, arr ) {
        return reduceCallback( prevVal, item, obj[item], obj );
    }, initialValue );
};

ตัวอย่างการใช้

var dataset = {
    key1 : 'value1',
    key2 : 'value2',
    key3 : 'value3'
};

function reduceFn( prevVal, key, val, obj ) {
    return prevVal + key + ' : ' + val + '; ';
}

console.log( dataset.reduce( reduceFn, 'initialValue' ) );
'Output' == 'initialValue; key1 : value1; key2 : value2; key3 : value3; '.

ไม่มีเลยพวกมัน !! ;-)


-1 ตอนนี้คุณมีคุณสมบัติที่นับได้ใหม่บนวัตถุในอนาคตทั้งหมด: jsfiddle.net/ygonjooh
Johan


1
โปรดอย่าดัดแปลงต้นแบบพื้นฐานเช่นนี้ สิ่งนี้สามารถนำไปสู่ปัญหามากมายสำหรับนักพัฒนาในอนาคตที่ทำงานในรหัสฐานเดียวกัน
adam.k

ใช่มันคือ "การปะของลิง" วิธีแก้ปัญหานี้เขียนขึ้นเมื่อ 6 ปีที่แล้วและไม่เกี่ยวข้องเกินไปตอนนี้โปรดจำไว้และตัวอย่างเช่นมันจะดีกว่าที่จะใช้Object.entries()ใน 2021
user1247458

2

คุณสามารถใช้นิพจน์ตัวสร้าง (สนับสนุนในเบราว์เซอร์ทั้งหมดสำหรับปีนี้และในโหนด) เพื่อรับคู่คีย์ - ค่าในรายการที่คุณสามารถลด:

>>> a = {"b": 3}
Object { b=3}

>>> [[i, a[i]] for (i in a) if (a.hasOwnProperty(i))]
[["b", 3]]

2

วัตถุสามารถเปลี่ยนเป็นอาร์เรย์ได้ด้วย: Object.entries () , Object.keys () , Object.values ​​()จากนั้นลดขนาดเป็นอาร์เรย์ แต่คุณสามารถลดวัตถุได้โดยไม่ต้องสร้างอาร์เรย์กลาง

ฉันได้สร้างห้องสมุดเล็ก ๆ น้อย ๆ ผู้ช่วยodictสำหรับการทำงานกับวัตถุ

npm install --save odict

มันมีreduceฟังก์ชั่นที่ใช้งานได้ดีเหมือนArray.prototype.reduce () :

export const reduce = (dict, reducer, accumulator) => {
  for (const key in dict)
    accumulator = reducer(accumulator, dict[key], key, dict);
  return accumulator;
};

คุณสามารถกำหนดให้กับ:

Object.reduce = reduce;

เนื่องจากวิธีนี้มีประโยชน์มาก!

ดังนั้นคำตอบสำหรับคำถามของคุณคือ:

const result = Object.reduce(
  {
    a: {value:1},
    b: {value:2},
    c: {value:3},
  },
  (accumulator, current) => (accumulator.value += current.value, accumulator), // reducer function must return accumulator
  {value: 0} // initial accumulator value
);

1

หากคุณสามารถใช้อาร์เรย์ได้ให้ใช้อาร์เรย์ความยาวและลำดับของอาร์เรย์จะมีค่าครึ่งหนึ่ง

function reducer(obj, fun, temp){
    if(typeof fun=== 'function'){
        if(temp== undefined) temp= '';
        for(var p in obj){
            if(obj.hasOwnProperty(p)){
                temp= fun(obj[p], temp, p, obj);
            }
        }
    }
    return temp;
}
var O={a:{value:1},b:{value:2},c:{value:3}}

reducer(O, function(a, b){return a.value+b;},0);

/ * ค่าที่ส่งคืน: (หมายเลข) 6 * /


1

นี่ไม่ใช่เรื่องยากที่จะใช้ตัวคุณเอง:

function reduceObj(obj, callback, initial) {
    "use strict";
    var key, lastvalue, firstIteration = true;
    if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function');
    }   
    if (arguments.length > 2) {
        // initial value set
        firstIteration = false;
        lastvalue = initial;
    }
    for (key in obj) {
        if (!obj.hasOwnProperty(key)) continue;
        if (firstIteration)
            firstIteration = false;
            lastvalue = obj[key];
            continue;
        }
        lastvalue = callback(lastvalue, obj[key], key, obj);
    }
    if (firstIteration) {
        throw new TypeError('Reduce of empty object with no initial value');
    }
    return lastvalue;
}

ในการดำเนินการ:

var o = {a: {value:1}, b: {value:2}, c: {value:3}};
reduceObj(o, function(prev, curr) { prev.value += cur.value; return prev;}, {value:0});
reduceObj(o, function(prev, curr) { return {value: prev.value + curr.value};});
// both == { value: 6 };

reduceObj(o, function(prev, curr) { return prev + curr.value; }, 0);
// == 6

คุณยังสามารถเพิ่มลงในต้นแบบวัตถุ:

if (typeof Object.prototype.reduce !== 'function') {
    Object.prototype.reduce = function(callback, initial) {
        "use strict";
        var args = Array.prototype.slice(arguments);
        args.unshift(this);
        return reduceObj.apply(null, args);
    }
}

0

เนื่องจากมันยังไม่ได้รับการยืนยันในคำตอบจริงๆอันเดอร์คอร์reduceก็ใช้งานได้เช่นกัน

_.reduce({ 
    a: {value:1}, 
    b: {value:2}, 
    c: {value:3} 
}, function(prev, current){
    //prev is either first object or total value
    var total = prev.value || prev

    return total + current.value
})

หมายเหตุ_.reduceจะส่งคืนค่าเดียว (วัตถุหรืออย่างอื่น) ถ้าวัตถุรายการมีเพียงหนึ่งรายการโดยไม่เรียกฟังก์ชันตัววนซ้ำ

_.reduce({ 
    a: {value:1} 
}, function(prev, current){
    //not called
})

//returns {value: 1} instead of 1

"ลองห้องสมุดอื่น ๆ นี้" ไม่มีประโยชน์
Grunion Shaftoe

ไม่มีประโยชน์กับคุณ
Chris Dolphin


0

ลองอันนี้. มันจะเรียงลำดับตัวเลขจากตัวแปรอื่น ๆ

const obj = {
   a: 1,
   b: 2,
   c: 3
};
const result = Object.keys(obj)
.reduce((acc, rec) => typeof obj[rec] === "number" ? acc.concat([obj[rec]]) : acc, [])
.reduce((acc, rec) => acc + rec)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.