จะทำการเปรียบเทียบอย่างลึกระหว่างวัตถุ 2 ชิ้นกับ lodash ได้อย่างไร?


309

ฉันมีวัตถุซ้อนกัน 2 ชิ้นซึ่งแตกต่างกันและฉันจำเป็นต้องรู้ว่าพวกเขามีความแตกต่างในหนึ่งในคุณสมบัติซ้อน

var a = {};
var b = {};

a.prop1 = 2;
a.prop2 = { prop3: 2 };

b.prop1 = 2;
b.prop2 = { prop3: 3 };

วัตถุอาจมีความซับซ้อนมากขึ้นพร้อมคุณสมบัติซ้อนกันมากขึ้น แต่อันนี้เป็นตัวอย่างที่ดี ฉันมีตัวเลือกให้ใช้ฟังก์ชั่นวนซ้ำหรือบางอย่างกับ lodash ...



7
_.isEqual(value, other)ทำการเปรียบเทียบเชิงลึกระหว่างสองค่าเพื่อตรวจสอบว่าพวกเขาจะเทียบเท่า lodash.com/docs#isEqual
Lukas Liesis

JSON.stringify ()
xgqfrms

10
! JSON.stringify () เป็นความผิด: JSON.stringify ({a: 1, b: 2}) == JSON.stringify ({b: 2: 1 ที่})
Shl

คำตอบ:


475

ทางออกที่ง่ายและสง่างามคือการใช้งาน_.isEqualซึ่งทำการเปรียบเทียบอย่างลึกซึ้ง:

var a = {};
var b = {};

a.prop1 = 2;
a.prop2 = { prop3: 2 };

b.prop1 = 2;
b.prop2 = { prop3: 3 };

_.isEqual(a, b); // returns false if different

อย่างไรก็ตามวิธีนี้ไม่ได้แสดงว่าทรัพย์สินใดแตกต่างกัน

http://jsfiddle.net/bdkeyn0h/


2
ฉันรู้ว่าคำตอบนั้นค่อนข้างเก่า แต่ฉันต้องการเพิ่มนั่น_.isEqualอาจเป็นเรื่องยาก หากคุณคัดลอกวัตถุและเปลี่ยนค่าบางอย่างในนั้นวัตถุนั้นจะยังคงแสดงอยู่เนื่องจากการอ้างอิงเหมือนกัน ดังนั้นควรใช้ฟังก์ชั่นนี้อย่างระมัดระวัง
oruckdeschel

23
@oruckdeschel หากการอ้างอิงเหมือนกันมันเป็นวัตถุเดียวกัน ดังนั้นจึงเท่ากับ นี่เป็นตัวชี้ที่ไม่ยุ่งยาก Lodash น่ากลัว
ผู้ชาย mograbi

265

หากคุณจำเป็นต้องรู้ว่าคุณสมบัติใดที่แตกต่างกันให้ใช้ลด () :

_.reduce(a, function(result, value, key) {
    return _.isEqual(value, b[key]) ?
        result : result.concat(key);
}, []);
// → [ "prop2" ]

36
โปรดทราบว่าสิ่งนี้จะส่งออกคุณสมบัติที่แตกต่างระดับแรกเท่านั้น (ดังนั้นจึงไม่ลึกมากในความรู้สึกของการแสดงผลคุณสมบัติที่แตกต่างกัน)
464 Bloke

16
นอกจากนี้สิ่งนี้จะไม่รับคุณสมบัติใน b ที่ไม่ได้อยู่ใน
Ed Staub

3
และ_.reduce(a, (result, value, key) => _.isEqual(value, b[key]) ? result : result.concat(key), [])สำหรับโซลูชัน ES6 บรรทัดเดียว
Dotgreg

1
รุ่นที่ต่อคีย์: ค่าlet edited = _.reduce(a, function(result, value, key) { return _.isEqual(value, b[key]) ? result : result.concat( { [key]: value } ); }, []);
Aline Matos

47

สำหรับทุกคนที่สะดุดกับหัวข้อนี้นี่เป็นทางออกที่สมบูรณ์มากขึ้น มันจะเปรียบเทียบวัตถุสองและให้คุณสำคัญของคุณสมบัติทั้งหมดที่มีอย่างใดอย่างหนึ่งเท่านั้นใน object1 , เฉพาะใน object2หรือทั้งในและ object1 object2 แต่มีค่าที่แตกต่าง :

/*
 * Compare two objects by reducing an array of keys in obj1, having the
 * keys in obj2 as the intial value of the result. Key points:
 *
 * - All keys of obj2 are initially in the result.
 *
 * - If the loop finds a key (from obj1, remember) not in obj2, it adds
 *   it to the result.
 *
 * - If the loop finds a key that are both in obj1 and obj2, it compares
 *   the value. If it's the same value, the key is removed from the result.
 */
function getObjectDiff(obj1, obj2) {
    const diff = Object.keys(obj1).reduce((result, key) => {
        if (!obj2.hasOwnProperty(key)) {
            result.push(key);
        } else if (_.isEqual(obj1[key], obj2[key])) {
            const resultKeyIndex = result.indexOf(key);
            result.splice(resultKeyIndex, 1);
        }
        return result;
    }, Object.keys(obj2));

    return diff;
}

นี่คือตัวอย่างผลลัพธ์:

// Test
let obj1 = {
    a: 1,
    b: 2,
    c: { foo: 1, bar: 2},
    d: { baz: 1, bat: 2 }
}

let obj2 = {
    b: 2, 
    c: { foo: 1, bar: 'monkey'}, 
    d: { baz: 1, bat: 2 }
    e: 1
}
getObjectDiff(obj1, obj2)
// ["c", "e", "a"]

หากคุณไม่สนใจเกี่ยวกับวัตถุที่ซ้อนกันและต้องการที่จะข้าม lodash คุณสามารถใช้แทนสำหรับการเปรียบเทียบค่าปกติเช่น_.isEqualobj1[key] === obj2[key]


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

อะไรคือความแตกต่างระหว่างการทำสิ่งนี้กับการใช้ _.isEqual (obj1, obj2) การเพิ่มการตรวจสอบสำหรับ hasOwnProperty ทำอะไรที่ _.isEqual ไม่ได้? ฉันอยู่ภายใต้การสันนิษฐานว่าถ้า obj1 มีคุณสมบัติที่ obj2 ไม่มีอยู่ _.isEqual จะไม่กลับมาจริง .. ?
Jaked222

2
@ Jaked222 - ความแตกต่างคือ isEqual คืนค่าบูลีนเพื่อบอกคุณว่าวัตถุเท่ากันหรือไม่ในขณะที่ฟังก์ชั่นด้านบนจะบอกคุณว่าอะไรคือความแตกต่างระหว่างวัตถุทั้งสอง (ถ้ามันแตกต่างกัน) หากคุณสนใจที่จะรู้ว่าวัตถุสองชิ้นนั้นเหมือนกันหรือไม่ isqual ก็เพียงพอแล้ว ในหลายกรณีแม้ว่าคุณต้องการทราบความแตกต่างระหว่างสองวัตถุ ตัวอย่างอาจเป็นถ้าคุณต้องการตรวจจับการเปลี่ยนแปลงก่อนและหลังบางสิ่งแล้วส่งเหตุการณ์ตามการเปลี่ยนแปลง
Johan Persson

30

ตามคำตอบของ Adam Boduchฉันเขียนฟังก์ชันนี้ซึ่งเปรียบเทียบสองวัตถุในความหมายที่ลึกที่สุดที่เป็นไปได้เส้นทางกลับที่มีค่าแตกต่างกันรวมถึงเส้นทางที่หายไปจากวัตถุหนึ่งหรือวัตถุอื่น

รหัสไม่ได้ถูกเขียนขึ้นโดยคำนึงถึงประสิทธิภาพและการปรับปรุงในเรื่องนั้นยินดีต้อนรับมากที่สุด แต่นี่คือรูปแบบพื้นฐาน:

var compare = function (a, b) {

  var result = {
    different: [],
    missing_from_first: [],
    missing_from_second: []
  };

  _.reduce(a, function (result, value, key) {
    if (b.hasOwnProperty(key)) {
      if (_.isEqual(value, b[key])) {
        return result;
      } else {
        if (typeof (a[key]) != typeof ({}) || typeof (b[key]) != typeof ({})) {
          //dead end.
          result.different.push(key);
          return result;
        } else {
          var deeper = compare(a[key], b[key]);
          result.different = result.different.concat(_.map(deeper.different, (sub_path) => {
            return key + "." + sub_path;
          }));

          result.missing_from_second = result.missing_from_second.concat(_.map(deeper.missing_from_second, (sub_path) => {
            return key + "." + sub_path;
          }));

          result.missing_from_first = result.missing_from_first.concat(_.map(deeper.missing_from_first, (sub_path) => {
            return key + "." + sub_path;
          }));
          return result;
        }
      }
    } else {
      result.missing_from_second.push(key);
      return result;
    }
  }, result);

  _.reduce(b, function (result, value, key) {
    if (a.hasOwnProperty(key)) {
      return result;
    } else {
      result.missing_from_first.push(key);
      return result;
    }
  }, result);

  return result;
}

คุณสามารถลองใช้รหัสโดยใช้ตัวอย่างนี้ (แนะนำให้ใช้ในโหมดเต็มหน้า):


4
ฉันเพียงแค่การแก้ไขข้อผิดพลาด แต่เพื่อให้คุณรู้ว่าคุณควรตรวจสอบการดำรงอยู่ที่สำคัญภายในวัตถุb ใช้b.hasOwnProperty(key)หรือkey in bb[key] != undefinedไม่ได้กับ กับรุ่นเก่าที่ใช้b[key] != undefinedฟังก์ชั่นส่งกลับต่างที่ไม่ถูกต้องสำหรับวัตถุที่มีในขณะที่undefined compare({disabled: undefined}, {disabled: undefined})ที่จริงแล้วเวอร์ชั่นเก่าก็มีปัญหาnullเช่นกัน คุณสามารถหลีกเลี่ยงปัญหาเช่นว่าโดยมักจะใช้===และ!==แทนและ== !=
Rory O'Kane

23

นี่เป็นวิธีแก้ปัญหาที่กระชับ:

_.differenceWith(a, b, _.isEqual);

7
ดูเหมือนจะไม่ทำงานกับวัตถุสำหรับฉัน ส่งคืนอาร์เรย์ว่างเปล่าแทน
tomhughes

2
ยังได้รับอาร์เรย์ว่างด้วย Lodash 4.17.4
aristidesfl

@ Z.Khullah ถ้ามันทำงานได้ในลักษณะนี้มันไม่ได้จัดทำเป็นเอกสาร
Brendon

1
@ Brendon, @THughes, @aristidesfl ขออภัยฉันได้ผสมสิ่งต่าง ๆ มันใช้งานได้กับอาร์เรย์ของวัตถุ แต่ไม่ใช่สำหรับการเปรียบเทียบวัตถุลึก มันจะเปิดออกถ้าพารามิเตอร์ไม่เป็นอาร์เรย์ lodash []ก็จะกลับมา
Z. Khullah

7

การซ้ำแสดงให้เห็นว่าวัตถุที่แตกต่างกันกับคนอื่น ๆ ที่คุณสามารถใช้_.reduceรวมกับ_.isEqualและ_.isPlainObject ในกรณีนี้คุณสามารถเปรียบเทียบความแตกต่างของ a กับ b หรือว่า b แตกต่างกับ a:

var a = {prop1: {prop1_1: 'text 1', prop1_2: 'text 2', prop1_3: [1, 2, 3]}, prop2: 2, prop3: 3};
var b = {prop1: {prop1_1: 'text 1', prop1_3: [1, 2]}, prop2: 2, prop3: 4};

var diff = function(obj1, obj2) {
  return _.reduce(obj1, function(result, value, key) {
    if (_.isPlainObject(value)) {
      result[key] = diff(value, obj2[key]);
    } else if (!_.isEqual(value, obj2[key])) {
      result[key] = value;
    }
    return result;
  }, {});
};

var res1 = diff(a, b);
var res2 = diff(b, a);
console.log(res1);
console.log(res2);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.4/lodash.min.js"></script>


7

_.isEqualวิธีการใช้งานง่ายมันจะทำงานสำหรับการเปรียบเทียบทั้งหมด ...

  • หมายเหตุ:วิธีนี้รองรับการเปรียบเทียบอาร์เรย์บัฟเฟอร์อาร์เรย์บูลีนวัตถุวันที่วัตถุข้อผิดพลาดแผนที่ตัวเลขObjectวัตถุ regexes ชุดเซตสตริงสัญลักษณ์และอาร์เรย์ที่พิมพ์ Objectวัตถุถูกเปรียบเทียบ * ด้วยคุณสมบัติของตัวเองที่ไม่สืบทอดและนับไม่ได้ ไม่รองรับฟังก์ชั่นและโหนด DOM *

ดังนั้นถ้าคุณมีด้านล่าง:

 const firstName = {name: "Alireza"};
 const otherName = {name: "Alireza"};

ถ้าคุณทำ: _.isEqual(firstName, otherName);,

มันจะกลับมาจริง

และถ้า const fullName = {firstName: "Alireza", familyName: "Dezfoolian"};

ถ้าคุณทำ: _.isEqual(firstName, fullName);,

จะกลับเท็จ


6

รหัสนี้ส่งคืนวัตถุที่มีคุณสมบัติทั้งหมดที่มีค่าแตกต่างกันและค่าของวัตถุทั้งสอง มีประโยชน์ในการบันทึกความแตกต่าง

var allkeys = _.union(_.keys(obj1), _.keys(obj2));
var difference = _.reduce(allkeys, function (result, key) {
  if ( !_.isEqual(obj1[key], obj2[key]) ) {
    result[key] = {obj1: obj1[key], obj2: obj2[key]}
  }
  return result;
}, {});

3

ฉันไม่ได้เขียนโค้ดนี้และใช้งานได้ดีสำหรับฉันเพื่อเปรียบเทียบเชิงลึกของ object1 กับ object2 โดยไม่ใช้ lodash / ขีดล่าง

function getObjectDiff(a, b) {
    var diffObj = {};
    if (Array.isArray(a)) {
        a.forEach(function(elem, index) {
            if (!Array.isArray(diffObj)) {
                diffObj = [];
            }
            diffObj[index] = getObjectDiff(elem, (b || [])[index]);
        });
    } else if (a != null && typeof a == 'object') {
        Object.keys(a).forEach(function(key) {
            if (Array.isArray(a[key])) {
                var arr = getObjectDiff(a[key], b[key]);
                if (!Array.isArray(arr)) {
                    arr = [];
                }
                arr.forEach(function(elem, index) {
                    if (!Array.isArray(diffObj[key])) {
                        diffObj[key] = [];
                    }
                    diffObj[key][index] = elem;
                });
            } else if (typeof a[key] == 'object') {
                diffObj[key] = getObjectDiff(a[key], b[key]);
            } else if (a[key] != (b || {})[key]) {
                diffObj[key] = a[key];
            } else if (a[key] == (b || {})[key]) {
                delete a[key];
            }
        });
    }
    Object.keys(diffObj).forEach(function(key) {
        if (typeof diffObj[key] == 'object' && JSON.stringify(diffObj[key]) == '{}') {
            delete diffObj[key];
        }
    });
    return diffObj;
}

3

เปรียบเทียบแบบลึกโดยใช้เทมเพลตของคุณสมบัติ (ซ้อน) เพื่อตรวจสอบ

function objetcsDeepEqualByTemplate(objectA, objectB, comparisonTemplate) {
  if (!objectA || !objectB) return false

  let areDifferent = false
  Object.keys(comparisonTemplate).some((key) => {
    if (typeof comparisonTemplate[key] === 'object') {
      areDifferent = !objetcsDeepEqualByTemplate(objectA[key], objectB[key], comparisonTemplate[key])
      return areDifferent
    } else if (comparisonTemplate[key] === true) {
      areDifferent = objectA[key] !== objectB[key]
      return areDifferent
    } else {
      return false
    }
  })

  return !areDifferent
}

const objA = { 
  a: 1,
  b: {
    a: 21,
    b: 22,
  },
  c: 3,
}

const objB = { 
  a: 1,
  b: {
    a: 21,
    b: 25,
  },
  c: true,
}

// template tells which props to compare
const comparisonTemplateA = {
  a: true,
  b: {
    a: true
  }
}
objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateA)
// returns true

const comparisonTemplateB = {
  a: true,
  c: true
}
// returns false
objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateB)

สิ่งนี้จะทำงานในคอนโซล สามารถเพิ่มการรองรับ Array ได้หากจำเป็น


2

ฉันเอารหัสของ Adam Boduch ไปแทงเอาท์พุทที่ลึกลงไป - นี่มันยังไม่ผ่านการทดสอบทั้งหมด แต่มีชิ้นส่วนอยู่:

function diff (obj1, obj2, path) {
    obj1 = obj1 || {};
    obj2 = obj2 || {};

    return _.reduce(obj1, function(result, value, key) {
        var p = path ? path + '.' + key : key;
        if (_.isObject(value)) {
            var d = diff(value, obj2[key], p);
            return d.length ? result.concat(d) : result;
        }
        return _.isEqual(value, obj2[key]) ? result : result.concat(p);
    }, []);
}

diff({ foo: 'lol', bar: { baz: true }}, {}) // returns ["foo", "bar.baz"]

1
ใช้งานได้อย่างมีเสน่ห์เพียงแค่ลำดับ obj1 และ obj2 นั้นสำคัญ ตัวอย่างเช่น: diff({}, { foo: 'lol', bar: { baz: true }}) // returns []
amangpt777

2

ตามที่ถูกถามนี่เป็นฟังก์ชันเปรียบเทียบวัตถุแบบเรียกซ้ำ และอีกเล็กน้อย สมมติว่าการใช้งานหลักของฟังก์ชั่นดังกล่าวคือการตรวจสอบวัตถุฉันมีสิ่งที่จะพูด การเปรียบเทียบเชิงลึกอย่างสมบูรณ์เป็นความคิดที่ไม่ดีเมื่อความแตกต่างบางอย่างไม่เกี่ยวข้อง ตัวอย่างเช่นการเปรียบเทียบความลึกของคนตาบอดในการยืนยัน TDD ทำให้การทดสอบที่ไม่จำเป็นเปราะ สำหรับเหตุผลที่ผมอยากจะแนะนำให้มีคุณค่ามากขึ้นdiff บางส่วน มันเป็นอะนาล็อกแบบเรียกซ้ำของการสนับสนุนก่อนหน้าในหัวข้อนี้ จะละเว้นคีย์ไม่ได้อยู่ใน

var bdiff = (a, b) =>
    _.reduce(a, (res, val, key) =>
        res.concat((_.isPlainObject(val) || _.isArray(val)) && b
            ? bdiff(val, b[key]).map(x => key + '.' + x) 
            : (!b || val != b[key] ? [key] : [])),
        []);

BDiff อนุญาตให้ตรวจสอบค่าที่คาดหวังในขณะที่ยอมรับคุณสมบัติอื่น ๆ ซึ่งเป็นสิ่งที่คุณต้องการสำหรับการตรวจสอบอัตโนมัติ สิ่งนี้ทำให้สามารถสร้างการยืนยันขั้นสูงทุกชนิด ตัวอย่างเช่น:

var diff = bdiff(expected, actual);
// all expected properties match
console.assert(diff.length == 0, "Objects differ", diff, expected, actual);
// controlled inequality
console.assert(diff.length < 3, "Too many differences", diff, expected, actual);

กลับไปที่โซลูชันที่สมบูรณ์ การสร้างความแตกต่างแบบดั้งเดิมกับ bdiff เป็นเรื่องเล็กน้อย:

function diff(a, b) {
    var u = bdiff(a, b), v = bdiff(b, a);
    return u.filter(x=>!v.includes(x)).map(x=>' < ' + x)
    .concat(u.filter(x=>v.includes(x)).map(x=>' | ' + x))
    .concat(v.filter(x=>!u.includes(x)).map(x=>' > ' + x));
};

การเรียกใช้ฟังก์ชันด้านบนของวัตถุที่ซับซ้อนสองรายการจะให้ผลลัพธ์คล้ายกัน

 [
  " < components.0.components.1.components.1.isNew",
  " < components.0.cryptoKey",
  " | components.0.components.2.components.2.components.2.FFT.min",
  " | components.0.components.2.components.2.components.2.FFT.max",
  " > components.0.components.1.components.1.merkleTree",
  " > components.0.components.2.components.2.components.2.merkleTree",
  " > components.0.components.3.FFTResult"
 ]

ในที่สุดเพื่อที่จะได้เห็นว่าค่าแตกต่างกันอย่างไรเราอาจต้องการEval ()เอาท์พุตโดยตรง สำหรับสิ่งนั้นเราต้องการbdiffรุ่น uglier ที่ให้เส้นทางที่ถูกต้องทางไวยากรณ์:

// provides syntactically correct output
var bdiff = (a, b) =>
    _.reduce(a, (res, val, key) =>
        res.concat((_.isPlainObject(val) || _.isArray(val)) && b
            ? bdiff(val, b[key]).map(x => 
                key + (key.trim ? '':']') + (x.search(/^\d/)? '.':'[') + x)
            : (!b || val != b[key] ? [key + (key.trim ? '':']')] : [])),
        []);

// now we can eval output of the diff fuction that we left unchanged
diff(a, b).filter(x=>x[1] == '|').map(x=>[x].concat([a, b].map(y=>((z) =>eval('z.' + x.substr(3))).call(this, y)))));

ที่จะส่งออกสิ่งที่คล้ายกับนี้:

[" | components[0].components[2].components[2].components[2].FFT.min", 0, 3]
[" | components[0].components[2].components[2].components[2].FFT.max", 100, 50]

ใบอนุญาต MIT;)


1

เมื่อทำคำตอบจากอดัมโบดัชเสร็จแล้วสิ่งนี้จะนำไปสู่ความแตกต่างของคุณสมบัติ

const differenceOfKeys = (...objects) =>
  _.difference(...objects.map(obj => Object.keys(obj)));
const differenceObj = (a, b) => 
  _.reduce(a, (result, value, key) => (
    _.isEqual(value, b[key]) ? result : [...result, key]
  ), differenceOfKeys(b, a));


0

นี่คือ typescript ที่เรียบง่ายพร้อมตัวตรวจสอบความแตกต่างลึกของ Lodash ซึ่งจะสร้างวัตถุใหม่ที่มีเพียงความแตกต่างระหว่างวัตถุเก่ากับวัตถุใหม่

ตัวอย่างเช่นถ้าเรามี:

const oldData = {a: 1, b: 2};
const newData = {a: 1, b: 3};

วัตถุที่เกิดขึ้นจะเป็น:

const result: {b: 3};

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

import * as _ from "lodash";

export const objectDeepDiff = (data: object | any, oldData: object | any) => {
  const record: any = {};
  Object.keys(data).forEach((key: string) => {
    // Checks that isn't an object and isn't equal
    if (!(typeof data[key] === "object" && _.isEqual(data[key], oldData[key]))) {
      record[key] = data[key];
    }
    // If is an object, and the object isn't equal
    if ((typeof data[key] === "object" && !_.isEqual(data[key], oldData[key]))) {
      record[key] = objectDeepDiff(data[key], oldData[key]);
    }
  });
  return record;
};

-1
var isEqual = function(f,s) {
  if (f === s) return true;

  if (Array.isArray(f)&&Array.isArray(s)) {
    return isEqual(f.sort(), s.sort());
  }
  if (_.isObject(f)) {
    return isEqual(f, s);
  }
  return _.isEqual(f, s);
};

สิ่งนี้ไม่ถูกต้อง คุณไม่สามารถเปรียบเทียบวัตถุด้วย===โดยตรง{ a: 20 } === { a: 20 }จะคืนค่าเท็จเนื่องจากเป็นการเปรียบเทียบต้นแบบ วิธีที่เหมาะสมกว่าในการเปรียบเทียบวัตถุเป็นหลักคือห่อมันไว้JSON.stringify()
Herrgott

ถ้า (f === s) คืนค่าจริง - สำหรับการเรียกซ้ำเท่านั้น ใช่: 20} === {a: 20} จะคืนค่าเท็จและไปยังเงื่อนไขถัดไป
สงคราม

ทำไมไม่เท่านั้น_.isEqual(f, s)? :)
Herrgott

สิ่งนี้จะส่งผลให้เกิดการวนซ้ำแบบวนซ้ำไม่สิ้นสุดเพราะถ้าfเป็นวัตถุและคุณจะได้รับif (_.isObject(f))คุณเพียงแค่ย้อนกลับไปที่ฟังก์ชั่นแล้วกดที่จุดนั้นอีกครั้ง กันไปสำหรับf (Array.isArray(f)&&Array.isArray(s))
rady

-2

สิ่งนี้มีพื้นฐานอยู่บน@JLavoieโดยใช้ lodash

let differences = function (newObj, oldObj) {
      return _.reduce(newObj, function (result, value, key) {
        if (!_.isEqual(value, oldObj[key])) {
          if (_.isArray(value)) {
            result[key] = []
            _.forEach(value, function (innerObjFrom1, index) {
              if (_.isNil(oldObj[key][index])) {
                result[key].push(innerObjFrom1)
              } else {
                let changes = differences(innerObjFrom1, oldObj[key][index])
                if (!_.isEmpty(changes)) {
                  result[key].push(changes)
                }
              }
            })
          } else if (_.isObject(value)) {
            result[key] = differences(value, oldObj[key])
          } else {
            result[key] = value
          }
        }
        return result
      }, {})
    }

https://jsfiddle.net/EmilianoBarboza/0g0sn3b9/8/


-2

เพียงแค่ใช้วานิลลา js

let a = {};
let b = {};

a.prop1 = 2;
a.prop2 = { prop3: 2 };

b.prop1 = 2;
b.prop2 = { prop3: 3 };

JSON.stringify(a) === JSON.stringify(b);
// false
b.prop2 = { prop3: 2};

JSON.stringify(a) === JSON.stringify(b);
// true

ป้อนคำอธิบายรูปภาพที่นี่


1
วิธีนี้จะไม่บอกคุณว่าคุณลักษณะใดแตกต่างกัน
JLavoie

2
ในกรณีนี้ลำดับแอตทริบิวต์จะมีผลต่อผลลัพธ์
Victor Oliveira

-2

เพื่อสร้างคำตอบของ Sridhar Gudimelaที่นี่มีการอัปเดตในลักษณะที่จะทำให้ Flow มีความสุข:

"use strict"; /* @flow */



//  E X P O R T

export const objectCompare = (objectA: any, objectB: any) => {
  let diffObj = {};

  switch(true) {
    case (Array.isArray(objectA)):
      objectA.forEach((elem, index) => {
        if (!Array.isArray(diffObj))
          diffObj = [];

        diffObj[index] = objectCompare(elem, (objectB || [])[index]);
      });

      break;

    case (objectA !== null && typeof objectA === "object"):
      Object.keys(objectA).forEach((key: any) => {
        if (Array.isArray(objectA[key])) {
          let arr = objectCompare(objectA[key], objectB[key]);

          if (!Array.isArray(arr))
            arr = [];

          arr.forEach((elem, index) => {
            if (!Array.isArray(diffObj[key]))
              diffObj[key] = [];

            diffObj[key][index] = elem;
          });
        } else if (typeof objectA[key] === "object")
          diffObj[key] = objectCompare(objectA[key], objectB[key]);
        else if (objectA[key] !== (objectB || {})[key])
          diffObj[key] = objectA[key];
        else if (objectA[key] === (objectB || {})[key])
          delete objectA[key];
      });

      break;

    default:
      break;
  }

  Object.keys(diffObj).forEach((key: any) => {
    if (typeof diffObj[key] === "object" && JSON.stringify(diffObj[key]) === "{}")
      delete diffObj[key];
  });

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