ความลึกทั่วไประหว่างวัตถุสองชนิด


222

ฉันมีสองวัตถุ: และoldObjnewObj

ข้อมูลที่oldObjใช้ในการเติมแบบฟอร์มและnewObjเป็นผลมาจากการที่ผู้ใช้เปลี่ยนข้อมูลในแบบฟอร์มนี้และส่งมัน

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

ตอนนี้ฉันต้องไม่เพียงแค่คิดออกว่ามีการเปลี่ยนแปลงอะไร (เช่นในเพิ่ม / ปรับปรุง / ลบ) จากoldObjถึงnewObjแต่ยังวิธีที่ดีที่สุดแทน

จนถึงตอนนี้ความคิดของฉันคือการสร้างgenericDeepDiffBetweenObjectsวิธีการที่จะคืนค่าวัตถุในแบบฟอร์ม{add:{...},upd:{...},del:{...}}แต่ฉันก็คิดว่า: คนอื่นต้องมีสิ่งนี้มาก่อน

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

ปรับปรุง:

ฉันคิดว่าวิธีที่ดีกว่าในการแสดงข้อมูลที่อัปเดตโดยใช้โครงสร้างวัตถุเดียวกับnewObjแต่เปลี่ยนค่าคุณสมบัติทั้งหมดเป็นวัตถุในแบบฟอร์ม:

{type: '<update|create|delete>', data: <propertyValue>}

ดังนั้นถ้าnewObj.prop1 = 'new value'และoldObj.prop1 = 'old value'มันจะตั้งreturnObj.prop1 = {type: 'update', data: 'new value'}

อัปเดต 2:

มันมีขนดกอย่างแท้จริงเมื่อเราไปถึงคุณสมบัติที่เป็นอาร์เรย์เนื่องจากอาร์เรย์[1,2,3]ควรถูกนับเท่ากับ[2,3,1]ซึ่งง่ายพอสำหรับอาร์เรย์ของชนิดที่มีค่าตามตัวอักษรเช่นสตริง int & bool แต่ยากที่จะจัดการเมื่อมันมาถึง อาร์เรย์ของประเภทการอ้างอิงเช่นวัตถุและอาร์เรย์

ตัวอย่างอาร์เรย์ที่ควรมีค่าเท่ากัน:

[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]

ไม่เพียงมีความซับซ้อนในการตรวจสอบความเท่ากันของค่าลึกเช่นนี้เท่านั้น แต่ยังต้องหาวิธีที่ดีในการแสดงการเปลี่ยนแปลงที่อาจเกิดขึ้น


1
ความเป็นไปได้ที่ซ้ำกันของความแตกต่างในวัตถุ JSON โดยใช้ Javascript / JQuery
a'r

2
@ a'r: มันไม่ซ้ำกับstackoverflow.com/questions/1200562/ … - ฉันรู้วิธีสำรวจวัตถุฉันกำลังมองหางานศิลปะก่อนหน้านี้เนื่องจากมันไม่สำคัญและจะใช้เวลาจริงในการปรับใช้และฉัน ควรใช้ห้องสมุดมากกว่าทำจากศูนย์
Martin Jespersen

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

1
@sbgoran: newObjสร้างขึ้นโดยค่าการอ่านโค้ด js จากแบบฟอร์มใน DOM มีหลายวิธีในการรักษาสถานะและทำสิ่งนี้ได้ง่ายกว่ามาก แต่ฉันต้องการทำให้ไร้สัญชาติเป็นการออกกำลังกาย นอกจากนี้ฉันกำลังมองหางานศิลปะก่อนหน้าเพื่อดูว่าคนอื่น ๆ อาจจะจัดการกับเรื่องนี้ได้อย่างไรถ้าใครมี
Martin Jespersen

3
นี่คือห้องสมุดที่มีความซับซ้อนมากที่จะกระจาย / แก้ไขวัตถุ Javascript คู่ใด ๆgithub.com/benjamine/jsondiffpatchคุณสามารถดูได้ที่นี่: benjamine.github.io/jsondiffpatch/demo/index.html (ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียน)
Benja

คำตอบ:


142

ผมเขียนระดับเล็ก ๆ น้อย ๆ ที่จะทำในสิ่งที่คุณต้องการคุณสามารถทดสอบได้ที่นี่

สิ่งเดียวที่แตกต่างจากข้อเสนอของคุณคือฉันไม่ถือว่า[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]เหมือนกันเพราะฉันคิดว่าอาร์เรย์ไม่เท่ากันถ้าลำดับขององค์ประกอบไม่เหมือนกัน แน่นอนว่าสามารถเปลี่ยนแปลงได้หากจำเป็น นอกจากนี้โค้ดนี้ยังสามารถปรับปรุงเพิ่มเติมให้ทำหน้าที่เป็นอาร์กิวเมนต์ที่จะใช้ในการจัดรูปแบบวัตถุ diff ด้วยวิธีการตามอำเภอใจตามค่าดั้งเดิมที่ส่งผ่าน (ตอนนี้งานนี้ทำโดยวิธี "comparValues")

var deepDiffMapper = function () {
  return {
    VALUE_CREATED: 'created',
    VALUE_UPDATED: 'updated',
    VALUE_DELETED: 'deleted',
    VALUE_UNCHANGED: 'unchanged',
    map: function(obj1, obj2) {
      if (this.isFunction(obj1) || this.isFunction(obj2)) {
        throw 'Invalid argument. Function given, object expected.';
      }
      if (this.isValue(obj1) || this.isValue(obj2)) {
        return {
          type: this.compareValues(obj1, obj2),
          data: obj1 === undefined ? obj2 : obj1
        };
      }

      var diff = {};
      for (var key in obj1) {
        if (this.isFunction(obj1[key])) {
          continue;
        }

        var value2 = undefined;
        if (obj2[key] !== undefined) {
          value2 = obj2[key];
        }

        diff[key] = this.map(obj1[key], value2);
      }
      for (var key in obj2) {
        if (this.isFunction(obj2[key]) || diff[key] !== undefined) {
          continue;
        }

        diff[key] = this.map(undefined, obj2[key]);
      }

      return diff;

    },
    compareValues: function (value1, value2) {
      if (value1 === value2) {
        return this.VALUE_UNCHANGED;
      }
      if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
        return this.VALUE_UNCHANGED;
      }
      if (value1 === undefined) {
        return this.VALUE_CREATED;
      }
      if (value2 === undefined) {
        return this.VALUE_DELETED;
      }
      return this.VALUE_UPDATED;
    },
    isFunction: function (x) {
      return Object.prototype.toString.call(x) === '[object Function]';
    },
    isArray: function (x) {
      return Object.prototype.toString.call(x) === '[object Array]';
    },
    isDate: function (x) {
      return Object.prototype.toString.call(x) === '[object Date]';
    },
    isObject: function (x) {
      return Object.prototype.toString.call(x) === '[object Object]';
    },
    isValue: function (x) {
      return !this.isObject(x) && !this.isArray(x);
    }
  }
}();


var result = deepDiffMapper.map({
  a: 'i am unchanged',
  b: 'i am deleted',
  e: {
    a: 1,
    b: false,
    c: null
  },
  f: [1, {
    a: 'same',
    b: [{
      a: 'same'
    }, {
      d: 'delete'
    }]
  }],
  g: new Date('2017.11.25')
}, {
  a: 'i am unchanged',
  c: 'i am created',
  e: {
    a: '1',
    b: '',
    d: 'created'
  },
  f: [{
    a: 'same',
    b: [{
      a: 'same'
    }, {
      c: 'create'
    }]
  }, 1],
  g: new Date('2017.11.25')
});
console.log(result);


3
+1 มันไม่ใช่โค้ดที่แย่ มีข้อผิดพลาด แต่ (ตรวจสอบตัวอย่างนี้ออก: jsfiddle.net/kySNu/3 cถูกสร้างขึ้นundefinedแต่ควรเป็นสตริง'i am created') และนอกจากนี้มันไม่ได้ทำสิ่งที่ฉันต้องการเพราะมันขาดการเปรียบเทียบค่าอาร์เรย์ลึกซึ่งเป็น ส่วนที่สำคัญที่สุด (และซับซ้อน / ยาก) ในฐานะที่เป็นด้านหมายเหตุการสร้าง'array' != typeof(obj)จะไร้ประโยชน์เนื่องจากอาร์เรย์เป็นวัตถุที่เป็นอินสแตนซ์ของอาร์เรย์
Martin Jespersen

1
ฉันอัปเดตโค้ดแล้ว แต่ฉันไม่แน่ใจว่าคุณต้องการค่าใดในวัตถุที่เป็นผลลัพธ์ตอนนี้โค้ดจะคืนค่าจากวัตถุแรกและหากไม่มีค่าจากวัตถุที่สองจะถูกตั้งค่าเป็นข้อมูล
sbgoran

1
และคุณหมายถึง "การขาดค่าการเปรียบเทียบอาร์เรย์ลึก" สำหรับอาร์เรย์ที่คุณจะได้รับสำหรับแต่ละดัชนีที่เป็น{type: ..., data:..}วัตถุ สิ่งที่ขาดหายไปคือการค้นหาค่าจากแถวลำดับที่หนึ่งในสอง แต่ตามที่ฉันพูดถึงในคำตอบของฉันฉันไม่คิดว่าอาร์เรย์มีค่าเท่ากันถ้าลำดับของค่าไม่เท่ากัน ( [1, 2, 3] is not equal to [3, 2, 1]ในความคิดของฉัน)
sbgoran

6
@MartinJespersen ตกลงคุณจะปฏิบัติกับอาร์เรย์นี้โดยทั่วไป[{key: 'value1'}] and [{key: 'value2'}, {key: 'value3'}]อย่างไร: ตอนนี้เป็นวัตถุแรกในอาร์เรย์แรกที่อัปเดตด้วย "value1" หรือ "value2" และนี่คือตัวอย่างง่าย ๆ มันอาจซับซ้อนมากเมื่อมีการทำรังลึก หากคุณต้องการ / {inner: {key: 'value1'}} and {inner: {key: 'value2'}, otherInner: {key: 'value3'}}ต้องเปรียบเทียบรังลึกไม่คำนึงถึงตำแหน่งที่สำคัญไม่ได้สร้างอาร์เรย์ของวัตถุสร้างวัตถุกับวัตถุที่ซ้อนกันอย่างเช่นก่อนหน้านี้:
sbgoran

2
ฉันเห็นด้วยกับคุณในมุมมองสุดท้าย - โครงสร้างข้อมูลดั้งเดิมควรเปลี่ยนเป็นสิ่งที่ง่ายกว่าในการทำส่วนต่างที่เกิดขึ้นจริง ขอแสดงความยินดีคุณจับมัน :)
Martin Jespersen

88

ใช้เครื่องหมายขีดล่างส่วนต่างที่เรียบง่าย:

var o1 = {a: 1, b: 2, c: 2},
    o2 = {a: 2, b: 1, c: 2};

_.omit(o1, function(v,k) { return o2[k] === v; })

ผลลัพธ์ในส่วนของo1ที่สอดคล้องกัน แต่มีค่าต่างกันในo2:

{a: 1, b: 2}

มันจะแตกต่างกันสำหรับ diff ลึก:

function diff(a,b) {
    var r = {};
    _.each(a, function(v,k) {
        if(b[k] === v) return;
        // but what if it returns an empty object? still attach?
        r[k] = _.isObject(v)
                ? _.diff(v, b[k])
                : v
            ;
        });
    return r;
}

ดังที่ @Juhana ชี้ให้เห็นในความคิดเห็นข้างต้นเป็นเพียงความแตกต่าง -> b และไม่สามารถย้อนกลับได้ (หมายถึงคุณสมบัติพิเศษใน b จะถูกละเว้น) ใช้แทน -> b -> a:

(function(_) {
  function deepDiff(a, b, r) {
    _.each(a, function(v, k) {
      // already checked this or equal...
      if (r.hasOwnProperty(k) || b[k] === v) return;
      // but what if it returns an empty object? still attach?
      r[k] = _.isObject(v) ? _.diff(v, b[k]) : v;
    });
  }

  /* the function */
  _.mixin({
    diff: function(a, b) {
      var r = {};
      deepDiff(a, b, r);
      deepDiff(b, a, r);
      return r;
    }
  });
})(_.noConflict());

ดูhttp://jsfiddle.net/drzaus/9g5qoxwj/สำหรับตัวอย่างแบบเต็ม + การทดสอบ + มิกซ์อิน


ไม่แน่ใจว่าทำไมคุณถึงถูก downvote นี่ก็เพียงพอแล้วเมื่อคุณให้ตัวอย่างตื้น ๆ ที่เรียบง่ายรวมถึงฟังก์ชั่นที่ซับซ้อนยิ่งขึ้น
Seiyria

2
@ ผู้เกลียดชังเซย์เรียจะเกลียดฉันเดาว่า ... ฉันทำทั้งคู่เพราะในตอนแรกฉันคิดว่าomitมันเป็นความแตกต่างที่ลึกล้ำ แต่ก็ผิดรวมอยู่ด้วยเพื่อการเปรียบเทียบ
drzaus

1
ทางออกที่ดี ฉันขอแนะนำให้มีการเปลี่ยนแปลงr[k] = ... : vในr[k] = ... : {'a':v, 'b':b[k] }วิธีนี้คุณสามารถดูสองค่า
guyaloni

2
ทั้งสองคนนี้กลับมาเป็นลบเท็จเมื่อวัตถุที่เหมือนกันเป็นอย่างอื่น แต่คนที่สองมีองค์ประกอบเช่นและ{a:1, b:2} {a:1, b:2, c:3}
JJJ

1
มันควรจะเป็นแทน_.omitBy _.omit
JP

48

ฉันต้องการนำเสนอโซลูชัน ES6 ... นี่คือการกระจายทางเดียวซึ่งหมายความว่าจะส่งคืนคีย์ / ค่าจากo2ที่ไม่เหมือนกันกับคู่ในo1:

let o1 = {
  one: 1,
  two: 2,
  three: 3
}

let o2 = {
  two: 2,
  three: 3,
  four: 4
}

let diff = Object.keys(o2).reduce((diff, key) => {
  if (o1[key] === o2[key]) return diff
  return {
    ...diff,
    [key]: o2[key]
  }
}, {})

3
วิธีแก้ปัญหาที่ดี แต่คุณอาจต้องการตรวจสอบif(o1[key] === o1[key])บรรทัดนั้น
bm_i

รหัสสมบูรณ์หรือไม่ ฉันจะได้รับUncaught SyntaxError: Unexpected token ...
Seano

2
ฉันชอบวิธีแก้ปัญหา แต่มีปัญหาหนึ่งอย่างถ้าวัตถุนั้นลึกกว่าระดับหนึ่งมันจะคืนค่าทั้งหมดในวัตถุที่ซ้อนกันที่เปลี่ยนไป - หรืออย่างน้อยนั่นคือสิ่งที่เกิดขึ้นกับฉัน
ปลอมเมื่อ

3
ใช่นี่ไม่ใช่แบบเรียกซ้ำ @Spurious
Nemesarial

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

22

ใช้ Lodash:

_.mergeWith(oldObj, newObj, function (objectValue, sourceValue, key, object, source) {
    if ( !(_.isEqual(objectValue, sourceValue)) && (Object(objectValue) !== objectValue)) {
        console.log(key + "\n    Expected: " + sourceValue + "\n    Actual: " + objectValue);
    }
});

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

คุณสามารถเพิ่มตรรกะภายในเพื่อจัดการอาร์เรย์ อาจเรียงลำดับอาร์เรย์ก่อน นี่เป็นทางออกที่ยืดหยุ่นมาก

แก้ไข

เปลี่ยนจาก _.merge เป็น _.mergeWith เนื่องจาก Lodash update ขอบคุณ Aviron ที่สังเกตเห็นการเปลี่ยนแปลง


6
ใน lodash 4.15.0 _.merge ด้วยฟังก์ชันตัวปรับแต่งไม่ได้รับการสนับสนุนอีกต่อไปดังนั้นคุณควรใช้ _.mergeWith แทน
Aviran Cohen

1
ฟังก์ชั่นนี้ยอดเยี่ยม แต่ใช้ไม่ได้กับวัตถุที่ซ้อนกัน
โจอัลเลน

13

นี่คือไลบรารี JavaScript ที่คุณสามารถใช้ค้นหาความแตกต่างระหว่างวัตถุ JavaScript สองตัว:

URL Github: https://github.com/cosmicanant/recursive-diff

Npmjs url: https://www.npmjs.com/package/recursive-diff

คุณสามารถใช้ไลบรารี่แบบเรียกซ้ำในเบราว์เซอร์เช่นเดียวกับ Node.js สำหรับเบราว์เซอร์ให้ทำดังนี้:

<script type="text" src="https://unpkg.com/recursive-diff@1.0.0/dist/recursive-diff.min.js"/>
<script type="text/javascript">
     const ob1 = {a:1, b: [2,3]};
     const ob2 = {a:2, b: [3,3,1]};
     const delta = recursiveDiff.getDiff(ob1,ob2); 
     /* console.log(delta) will dump following data 
     [
         {path: ['a'], op: 'update', val: 2}
         {path: ['b', '0'], op: 'update',val: 3},
         {path: ['b',2], op: 'add', val: 1 },
     ]
      */
     const ob3 = recursiveDiff.applyDiff(ob1, delta); //expect ob3 is deep equal to ob2
 </script>

โดยที่ใน node.js คุณสามารถต้องการโมดูล 'recursive-diff' และใช้งานได้ดังต่อไปนี้:

const diff = require('recursive-diff');
const ob1 = {a: 1}, ob2: {b:2};
const diff = diff.getDiff(ob1, ob2);

สิ่งนี้จะไม่บัญชีสำหรับการเปลี่ยนแปลงในคุณสมบัติของวันที่ตัวอย่างเช่น
trollkotze

มีการเพิ่มการสนับสนุนวันที่
อนันต์

9

ทุกวันนี้มีโมดูลจำนวนไม่มากนัก ฉันเพิ่งเขียนโมดูลเพื่อทำสิ่งนี้เพราะฉันไม่พอใจกับโมดูลที่แตกต่างจำนวนมากที่ฉันพบ ก็เรียกว่าodiff : https://github.com/Tixit/odiff ฉันยังแสดงรายการโมดูลยอดนิยมจำนวนมากและสาเหตุที่พวกเขาไม่ได้รับการยอมรับใน readme ของodiffซึ่งคุณสามารถตรวจสอบได้หากodiffไม่มีคุณสมบัติที่คุณต้องการ นี่คือตัวอย่าง:

var a = [{a:1,b:2,c:3},              {x:1,y: 2, z:3},              {w:9,q:8,r:7}]
var b = [{a:1,b:2,c:3},{t:4,y:5,u:6},{x:1,y:'3',z:3},{t:9,y:9,u:9},{w:9,q:8,r:7}]

var diffs = odiff(a,b)

/* diffs now contains:
[{type: 'add', path:[], index: 2, vals: [{t:9,y:9,u:9}]},
 {type: 'set', path:[1,'y'], val: '3'},
 {type: 'add', path:[], index: 1, vals: [{t:4,y:5,u:6}]}
]
*/

7
const diff = require("deep-object-diff").diff;
let differences = diff(obj2, obj1);

มีโมดูล npm พร้อมดาวน์โหลดมากกว่า 500k ครั้งต่อสัปดาห์คือ: https://www.npmjs.com/package/deep-object-diff

ฉันชอบวัตถุที่เป็นตัวแทนของความแตกต่าง - โดยเฉพาะอย่างยิ่งมันง่ายที่จะเห็นโครงสร้างเมื่อมันถูกจัดรูปแบบ

const diff = require("deep-object-diff").diff;

const lhs = {
  foo: {
    bar: {
      a: ['a', 'b'],
      b: 2,
      c: ['x', 'y'],
      e: 100 // deleted
    }
  },
  buzz: 'world'
};

const rhs = {
  foo: {
    bar: {
      a: ['a'], // index 1 ('b')  deleted
      b: 2, // unchanged
      c: ['x', 'y', 'z'], // 'z' added
      d: 'Hello, world!' // added
    }
  },
  buzz: 'fizz' // updated
};

console.log(diff(lhs, rhs)); // =>
/*
{
  foo: {
    bar: {
      a: {
        '1': undefined
      },
      c: {
        '2': 'z'
      },
      d: 'Hello, world!',
      e: undefined
    }
  },
  buzz: 'fizz'
}
*/

2

ฉันใช้โค้ดนี้สำหรับการทำงานที่คุณอธิบาย:

function mergeRecursive(obj1, obj2) {
    for (var p in obj2) {
        try {
            if(obj2[p].constructor == Object) {
                obj1[p] = mergeRecursive(obj1[p], obj2[p]);
            }
            // Property in destination object set; update its value.
            else if (Ext.isArray(obj2[p])) {
                // obj1[p] = [];
                if (obj2[p].length < 1) {
                    obj1[p] = obj2[p];
                }
                else {
                    obj1[p] = mergeRecursive(obj1[p], obj2[p]);
                }

            }else{
                obj1[p] = obj2[p];
            }
        } catch (e) {
            // Property in destination object not set; create it and set its value.
            obj1[p] = obj2[p];
        }
    }
    return obj1;
}

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


1
ฉันกำลังใช้กรอบต่อที่นี่ แต่คุณสามารถแทนที่และใช้สิ่งที่เคยกรอบอื่น ๆ ที่คุณต้องการ ...
aMember

การผสานวัตถุเป็นเรื่องเล็กน้อยและสามารถทำได้ง่ายเหมือนกับการ$.extend(true,obj1,obj2)ใช้ jQuery นี่ไม่ใช่สิ่งที่ฉันต้องการ ฉันต้องการความแตกต่างระหว่างวัตถุทั้งสองไม่ใช่การรวมกันของวัตถุเหล่านั้น
Martin Jespersen

มันยอดเยี่ยมมากที่มีการใช้งาน Ext ในที่นี่
เปอร์ออกไซด์

2

ฉันได้พัฒนาฟังก์ชั่นชื่อว่า "comparValue ()" ใน Javascript มันจะคืนค่าว่าเหมือนกันหรือไม่ ฉันเรียกว่า comparValue () ในการวนรอบของวัตถุหนึ่ง คุณสามารถรับความแตกต่างของวัตถุสองชนิดใน diffParams

var diffParams = {};
var obj1 = {"a":"1", "b":"2", "c":[{"key":"3"}]},
    obj2 = {"a":"1", "b":"66", "c":[{"key":"55"}]};

for( var p in obj1 ){
  if ( !compareValue(obj1[p], obj2[p]) ){
    diffParams[p] = obj1[p];
  }
}

function compareValue(val1, val2){
  var isSame = true;
  for ( var p in val1 ) {

    if (typeof(val1[p]) === "object"){
      var objectValue1 = val1[p],
          objectValue2 = val2[p];
      for( var value in objectValue1 ){
        isSame = compareValue(objectValue1[value], objectValue2[value]);
        if( isSame === false ){
          return false;
        }
      }
    }else{
      if(val1 !== val2){
        isSame = false;
      }
    }
  }
  return isSame;
}
console.log(diffParams);


1

ฉันรู้ว่าฉันมางานปาร์ตี้ช้า แต่ฉันต้องการอะไรที่คล้ายกันที่คำตอบข้างต้นไม่ได้ช่วย

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

นี่คือรหัส: https://jsfiddle.net/rv01x6jo/

นี่คือวิธีการใช้งาน:

// To only return the difference
var difference = diff(newValue, oldValue);  

// To exclude certain properties
var difference = diff(newValue, oldValue, [newValue.prop1, newValue.prop2, newValue.prop3]);

หวังว่านี่จะช่วยใครซักคน


โปรดใส่รหัสลงในคำตอบของคุณด้วยไม่ใช่แค่เล่นซอ
xpy

ดูเหมือน defineProperty จะแก้ปัญหานี้ด้วยประสิทธิภาพที่ดีขึ้นถ้าฉันจำได้อย่างถูกต้องมันทำงานได้จนถึง IE9
ปีเตอร์

ขอบคุณ .. !! รหัสของคุณทำงานได้อย่างมีเสน่ห์และบันทึกวันของฉัน ฉันมีวัตถุ json 1250 บรรทัดและให้ o / p ที่แน่นอนที่ฉันต้องการ
Tejas Mehta

1

ฉันใช้ ramda เพื่อแก้ไขปัญหาเดียวกันฉันจำเป็นต้องรู้ว่ามีอะไรเปลี่ยนแปลงในวัตถุใหม่ ดังนั้นที่นี่การออกแบบของฉัน

const oldState = {id:'170',name:'Ivab',secondName:'Ivanov',weight:45};
const newState = {id:'170',name:'Ivanko',secondName:'Ivanov',age:29};

const keysObj1 = R.keys(newState)

const filterFunc = key => {
  const value = R.eqProps(key,oldState,newState)
  return {[key]:value}
}

const result = R.map(filterFunc, keysObj1)

ผลลัพธ์คือชื่อของคุณสมบัติและสถานะ

[{"id":true}, {"name":false}, {"secondName":true}, {"age":false}]

1

นี่คือรุ่น typescript ของรหัส @sbgoran

export class deepDiffMapper {

  static VALUE_CREATED = 'created';
  static VALUE_UPDATED = 'updated';
  static VALUE_DELETED = 'deleted';
  static VALUE_UNCHANGED ='unchanged';

  protected isFunction(obj: object) {
    return {}.toString.apply(obj) === '[object Function]';
  };

  protected isArray(obj: object) {
      return {}.toString.apply(obj) === '[object Array]';
  };

  protected isObject(obj: object) {
      return {}.toString.apply(obj) === '[object Object]';
  };

  protected isDate(obj: object) {
      return {}.toString.apply(obj) === '[object Date]';
  };

  protected isValue(obj: object) {
      return !this.isObject(obj) && !this.isArray(obj);
  };

  protected compareValues (value1: any, value2: any) {
    if (value1 === value2) {
        return deepDiffMapper.VALUE_UNCHANGED;
    }
    if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
        return deepDiffMapper.VALUE_UNCHANGED;
    }
    if ('undefined' == typeof(value1)) {
        return deepDiffMapper.VALUE_CREATED;
    }
    if ('undefined' == typeof(value2)) {
        return deepDiffMapper.VALUE_DELETED;
    }

    return deepDiffMapper.VALUE_UPDATED;
  }

  public map(obj1: object, obj2: object) {
      if (this.isFunction(obj1) || this.isFunction(obj2)) {
          throw 'Invalid argument. Function given, object expected.';
      }
      if (this.isValue(obj1) || this.isValue(obj2)) {
          return {
              type: this.compareValues(obj1, obj2),
              data: (obj1 === undefined) ? obj2 : obj1
          };
      }

      var diff = {};
      for (var key in obj1) {
          if (this.isFunction(obj1[key])) {
              continue;
          }

          var value2 = undefined;
          if ('undefined' != typeof(obj2[key])) {
              value2 = obj2[key];
          }

          diff[key] = this.map(obj1[key], value2);
      }
      for (var key in obj2) {
          if (this.isFunction(obj2[key]) || ('undefined' != typeof(diff[key]))) {
              continue;
          }

          diff[key] = this.map(undefined, obj2[key]);
      }

      return diff;

  }
}

1

นี่เป็นรุ่นที่ปรับเปลี่ยนบางสิ่งบางอย่างที่พบในgisthub

isNullBlankOrUndefined = function (o) {
    return (typeof o === "undefined" || o == null || o === "");
}

/**
 * Deep diff between two object, using lodash
 * @param  {Object} object Object compared
 * @param  {Object} base   Object to compare with
 * @param  {Object} ignoreBlanks will not include properties whose value is null, undefined, etc.
 * @return {Object}        Return a new object who represent the diff
 */
objectDifference = function (object, base, ignoreBlanks = false) {
    if (!lodash.isObject(object) || lodash.isDate(object)) return object            // special case dates
    return lodash.transform(object, (result, value, key) => {
        if (!lodash.isEqual(value, base[key])) {
            if (ignoreBlanks && du.isNullBlankOrUndefined(value) && isNullBlankOrUndefined( base[key])) return;
            result[key] = lodash.isObject(value) && lodash.isObject(base[key]) ? objectDifference(value, base[key]) : value;
        }
    });
}

1

ฉันแก้ไขคำตอบของ @ sbgoran เพื่อให้วัตถุ diff ที่เกิดขึ้นรวมเฉพาะค่าที่เปลี่ยนแปลงและละเว้นค่าที่เหมือนกัน นอกจากนี้ยังแสดงให้เห็นว่าทั้งค่าเดิมและความคุ้มค่าที่อัปเดต

var deepDiffMapper = function () {
    return {
        VALUE_CREATED: 'created',
        VALUE_UPDATED: 'updated',
        VALUE_DELETED: 'deleted',
        VALUE_UNCHANGED: '---',
        map: function (obj1, obj2) {
            if (this.isFunction(obj1) || this.isFunction(obj2)) {
                throw 'Invalid argument. Function given, object expected.';
            }
            if (this.isValue(obj1) || this.isValue(obj2)) {
                let returnObj = {
                    type: this.compareValues(obj1, obj2),
                    original: obj1,
                    updated: obj2,
                };
                if (returnObj.type != this.VALUE_UNCHANGED) {
                    return returnObj;
                }
                return undefined;
            }

            var diff = {};
            let foundKeys = {};
            for (var key in obj1) {
                if (this.isFunction(obj1[key])) {
                    continue;
                }

                var value2 = undefined;
                if (obj2[key] !== undefined) {
                    value2 = obj2[key];
                }

                let mapValue = this.map(obj1[key], value2);
                foundKeys[key] = true;
                if (mapValue) {
                    diff[key] = mapValue;
                }
            }
            for (var key in obj2) {
                if (this.isFunction(obj2[key]) || foundKeys[key] !== undefined) {
                    continue;
                }

                let mapValue = this.map(undefined, obj2[key]);
                if (mapValue) {
                    diff[key] = mapValue;
                }
            }

            //2020-06-13: object length code copied from https://stackoverflow.com/a/13190981/2336212
            if (Object.keys(diff).length > 0) {
                return diff;
            }
            return undefined;
        },
        compareValues: function (value1, value2) {
            if (value1 === value2) {
                return this.VALUE_UNCHANGED;
            }
            if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
                return this.VALUE_UNCHANGED;
            }
            if (value1 === undefined) {
                return this.VALUE_CREATED;
            }
            if (value2 === undefined) {
                return this.VALUE_DELETED;
            }
            return this.VALUE_UPDATED;
        },
        isFunction: function (x) {
            return Object.prototype.toString.call(x) === '[object Function]';
        },
        isArray: function (x) {
            return Object.prototype.toString.call(x) === '[object Array]';
        },
        isDate: function (x) {
            return Object.prototype.toString.call(x) === '[object Date]';
        },
        isObject: function (x) {
            return Object.prototype.toString.call(x) === '[object Object]';
        },
        isValue: function (x) {
            return !this.isObject(x) && !this.isArray(x);
        }
    }
}();

0

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

ใน IE8 ทำงานได้ 100% ผ่านการทดสอบเรียบร้อยแล้ว

//  ObjectKey: ["DataType, DefaultValue"]
reference = { 
    a : ["string", 'Defaul value for "a"'],
    b : ["number", 300],
    c : ["boolean", true],
    d : {
        da : ["boolean", true],
        db : ["string", 'Defaul value for "db"'],
        dc : {
            dca : ["number", 200],
            dcb : ["string", 'Default value for "dcb"'],
            dcc : ["number", 500],
            dcd : ["boolean", true]
      },
      dce : ["string", 'Default value for "dce"'],
    },
    e : ["number", 200],
    f : ["boolean", 0],
    g : ["", 'This is an internal extra parameter']
};

userOptions = { 
    a : 999, //Only string allowed
  //b : ["number", 400], //User missed this parameter
    c: "Hi", //Only lower case or case insitive in quotes true/false allowed.
    d : {
        da : false,
        db : "HelloWorld",
        dc : {
            dca : 10,
            dcb : "My String", //Space is not allowed for ID attr
            dcc: "3thString", //Should not start with numbers
            dcd : false
      },
      dce: "ANOTHER STRING",
    },
    e: 40,
    f: true,
};


function compare(ref, obj) {

    var validation = {
        number: function (defaultValue, userValue) {
          if(/^[0-9]+$/.test(userValue))
            return userValue;
          else return defaultValue;
        },
        string: function (defaultValue, userValue) {
          if(/^[a-z][a-z0-9-_.:]{1,51}[^-_.:]$/i.test(userValue)) //This Regex is validating HTML tag "ID" attributes
            return userValue;
          else return defaultValue;
        },
        boolean: function (defaultValue, userValue) {
          if (typeof userValue === 'boolean')
            return userValue;
          else return defaultValue;
        }
    };

    for (var key in ref)
        if (obj[key] && obj[key].constructor && obj[key].constructor === Object)
          ref[key] = compare(ref[key], obj[key]);
        else if(obj.hasOwnProperty(key))
          ref[key] = validation[ref[key][0]](ref[key][1], obj[key]); //or without validation on user enties => ref[key] = obj[key]
        else ref[key] = ref[key][1];
    return ref;
}

//console.log(
    alert(JSON.stringify( compare(reference, userOptions),null,2 ))
//);

/* ผลลัพธ์

{
  "a": "Defaul value for \"a\"",
  "b": 300,
  "c": true,
  "d": {
    "da": false,
    "db": "Defaul value for \"db\"",
    "dc": {
      "dca": 10,
      "dcb": "Default value for \"dcb\"",
      "dcc": 500,
      "dcd": false
    },
    "dce": "Default value for \"dce\""
  },
  "e": 40,
  "f": true,
  "g": "This is an internal extra parameter"
}

*/

0

ฟังก์ชั่นที่ขยายและเรียบง่ายขึ้นจากคำตอบของ sbgoran
วิธีนี้จะช่วยให้สามารถสแกนได้ลึกและค้นหาความเหมือนกันของอาเรย์

var result = objectDifference({
      a:'i am unchanged',
      b:'i am deleted',
      e: {a: 1,b:false, c: null},
      f: [1,{a: 'same',b:[{a:'same'},{d: 'delete'}]}],
      g: new Date('2017.11.25'),
      h: [1,2,3,4,5]
  },
  {
      a:'i am unchanged',
      c:'i am created',
      e: {a: '1', b: '', d:'created'},
      f: [{a: 'same',b:[{a:'same'},{c: 'create'}]},1],
      g: new Date('2017.11.25'),
      h: [4,5,6,7,8]
  });
console.log(result);

function objectDifference(obj1, obj2){
    if((dataType(obj1) !== 'array' && dataType(obj1) !== 'object') || (dataType(obj2) !== 'array' && dataType(obj2) !== 'object')){
        var type = '';

        if(obj1 === obj2 || (dataType(obj1) === 'date' && dataType(obj2) === 'date' && obj1.getTime() === obj2.getTime()))
            type = 'unchanged';
        else if(dataType(obj1) === 'undefined')
            type = 'created';
        if(dataType(obj2) === 'undefined')
            type = 'deleted';
        else if(type === '') type = 'updated';

        return {
            type: type,
            data:(obj1 === undefined) ? obj2 : obj1
        };
    }
  
    if(dataType(obj1) === 'array' && dataType(obj2) === 'array'){
        var diff = [];
        obj1.sort(); obj2.sort();
        for(var i = 0; i < obj2.length; i++){
            var type = obj1.indexOf(obj2[i]) === -1?'created':'unchanged';
            if(type === 'created' && (dataType(obj2[i]) === 'array' || dataType(obj2[i]) === 'object')){
                diff.push(
                    objectDifference(obj1[i], obj2[i])
                );
                continue;
            }
            diff.push({
                type: type,
                data: obj2[i]
            });
        }

        for(var i = 0; i < obj1.length; i++){
            if(obj2.indexOf(obj1[i]) !== -1 || dataType(obj1[i]) === 'array' || dataType(obj1[i]) === 'object')
                continue;
            diff.push({
                type: 'deleted',
                data: obj1[i]
            });
        }
    } else {
        var diff = {};
        var key = Object.keys(obj1);
        for(var i = 0; i < key.length; i++){
            var value2 = undefined;
            if(dataType(obj2[key[i]]) !== 'undefined')
                value2 = obj2[key[i]];

            diff[key[i]] = objectDifference(obj1[key[i]], value2);
        }

        var key = Object.keys(obj2);
        for(var i = 0; i < key.length; i++){
            if(dataType(diff[key[i]]) !== 'undefined')
                continue;

            diff[key[i]] = objectDifference(undefined, obj2[key[i]]);
        }
    }

    return diff;
}

function dataType(data){
    if(data === undefined || data === null) return 'undefined';
    if(data.constructor === String) return 'string';
    if(data.constructor === Array) return 'array';
    if(data.constructor === Object) return 'object';
    if(data.constructor === Number) return 'number';
    if(data.constructor === Boolean) return 'boolean';
    if(data.constructor === Function) return 'function';
    if(data.constructor === Date) return 'date';
    if(data.constructor === RegExp) return 'regex';
    return 'unknown';
}


0

ฉันสะดุดที่นี่พยายามค้นหาวิธีที่จะได้รับความแตกต่างระหว่างวัตถุสองชิ้น นี่คือทางออกของฉันโดยใช้ Lodash:

// Get updated values (including new values)
var updatedValuesIncl = _.omitBy(curr, (value, key) => _.isEqual(last[key], value));

// Get updated values (excluding new values)
var updatedValuesExcl = _.omitBy(curr, (value, key) => (!_.has(last, key) || _.isEqual(last[key], value)));

// Get old values (by using updated values)
var oldValues = Object.keys(updatedValuesIncl).reduce((acc, key) => { acc[key] = last[key]; return acc; }, {});

// Get newly added values
var newCreatedValues = _.omitBy(curr, (value, key) => _.has(last, key));

// Get removed values
var deletedValues = _.omitBy(last, (value, key) => _.has(curr, key));

// Then you can group them however you want with the result

ข้อมูลโค้ดด้านล่าง:

var last = {
"authed": true,
"inForeground": true,
"goodConnection": false,
"inExecutionMode": false,
"online": true,
"array": [1, 2, 3],
"deep": {
	"nested": "value",
},
"removed": "value",
};

var curr = {
"authed": true,
"inForeground": true,
"deep": {
	"nested": "changed",
},
"array": [1, 2, 4],
"goodConnection": true,
"inExecutionMode": false,
"online": false,
"new": "value"
};

// Get updated values (including new values)
var updatedValuesIncl = _.omitBy(curr, (value, key) => _.isEqual(last[key], value));
// Get updated values (excluding new values)
var updatedValuesExcl = _.omitBy(curr, (value, key) => (!_.has(last, key) || _.isEqual(last[key], value)));
// Get old values (by using updated values)
var oldValues = Object.keys(updatedValuesIncl).reduce((acc, key) => { acc[key] = last[key]; return acc; }, {});
// Get newly added values
var newCreatedValues = _.omitBy(curr, (value, key) => _.has(last, key));
// Get removed values
var deletedValues = _.omitBy(last, (value, key) => _.has(curr, key));

console.log('oldValues', JSON.stringify(oldValues));
console.log('updatedValuesIncl', JSON.stringify(updatedValuesIncl));
console.log('updatedValuesExcl', JSON.stringify(updatedValuesExcl));
console.log('newCreatedValues', JSON.stringify(newCreatedValues));
console.log('deletedValues', JSON.stringify(deletedValues));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>


0

ฉันได้รับคำตอบข้างต้นโดย @sbgoran และแก้ไขสำหรับกรณีของฉันเช่นเดียวกับคำถามที่ต้องการเพื่อรักษาอาร์เรย์เป็นชุด (เช่นลำดับไม่สำคัญสำหรับ diff)

const deepDiffMapper = function () {
return {
  VALUE_CREATED: "created",
  VALUE_UPDATED: "updated",
  VALUE_DELETED: "deleted",
  VALUE_UNCHANGED: "unchanged",
  map: function(obj1: any, obj2: any) {
    if (this.isFunction(obj1) || this.isFunction(obj2)) {
      throw "Invalid argument. Function given, object expected.";
    }
    if (this.isValue(obj1) || this.isValue(obj2)) {
      return {
        type: this.compareValues(obj1, obj2),
        data: obj2 === undefined ? obj1 : obj2
      };
    }

    if (this.isArray(obj1) || this.isArray(obj2)) {
      return {
        type: this.compareArrays(obj1, obj2),
        data: this.getArrayDiffData(obj1, obj2)
      };
    }

    const diff: any = {};
    for (const key in obj1) {

      if (this.isFunction(obj1[key])) {
        continue;
      }

      let value2 = undefined;
      if (obj2[key] !== undefined) {
        value2 = obj2[key];
      }

      diff[key] = this.map(obj1[key], value2);
    }
    for (const key in obj2) {
      if (this.isFunction(obj2[key]) || diff[key] !== undefined) {
        continue;
      }

      diff[key] = this.map(undefined, obj2[key]);
    }

    return diff;

  },

  getArrayDiffData: function(arr1: Array<any>, arr2: Array<any>) {
    const set1 = new Set(arr1);
    const set2 = new Set(arr2);

    if (arr1 === undefined || arr2 === undefined) {
       return arr1 === undefined ? arr1 : arr2;
    }
    const deleted = [...arr1].filter(x => !set2.has(x));

    const added = [...arr2].filter(x => !set1.has(x));

    return {
      added, deleted
    };

  },

  compareArrays: function(arr1: Array<any>, arr2: Array<any>) {
    const set1 = new Set(arr1);
    const set2 = new Set(arr2);
    if (_.isEqual(_.sortBy(arr1), _.sortBy(arr2))) {
      return this.VALUE_UNCHANGED;
    }
    if (arr1 === undefined) {
      return this.VALUE_CREATED;
    }
    if (arr2 === undefined) {
      return this.VALUE_DELETED;
    }
    return this.VALUE_UPDATED;
  },
  compareValues: function (value1: any, value2: any) {
    if (value1 === value2) {
      return this.VALUE_UNCHANGED;
    }
    if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
      return this.VALUE_UNCHANGED;
    }
    if (value1 === undefined) {
      return this.VALUE_CREATED;
    }
    if (value2 === undefined) {
      return this.VALUE_DELETED;
    }
    return this.VALUE_UPDATED;
  },
  isFunction: function (x: any) {
    return Object.prototype.toString.call(x) === "[object Function]";
  },
  isArray: function (x: any) {
    return Object.prototype.toString.call(x) === "[object Array]";
  },
  isDate: function (x: any) {
    return Object.prototype.toString.call(x) === "[object Date]";
  },
  isObject: function (x: any) {
    return Object.prototype.toString.call(x) === "[object Object]";
  },
  isValue: function (x: any) {
    return !this.isObject(x) && !this.isArray(x);
  }
 };
}();

0

นี่คือทางออกที่:

  • typescript (แต่สามารถแปลงเป็น Javascript ได้ง่าย)
  • ไม่มีการพึ่งพา lib
  • ทั่วไปและไม่สนใจเกี่ยวกับการตรวจสอบประเภทวัตถุ (นอกเหนือจาก objectประเภท)
  • รองรับคุณสมบัติที่มีค่า undefined
  • ไม่ลึก (ค่าเริ่มต้น)

ก่อนอื่นเรากำหนดอินเทอร์เฟซผลการเปรียบเทียบ:

export interface ObjectComparison {
  added: {};
  updated: {
    [propName: string]: Change;
  };
  removed: {};
  unchanged: {};
}

ด้วยกรณีพิเศษของการเปลี่ยนแปลงที่เราต้องการทราบว่าค่าเก่าและค่าใหม่คืออะไร:

export interface Change {
  oldValue: any;
  newValue: any;
}

จากนั้นเราสามารถให้diffฟังก์ชั่นซึ่งเป็นเพียงสองลูป (ด้วยซ้ำถ้าdeepเป็นtrue):

export class ObjectUtils {

  static diff(o1: {}, o2: {}, deep = false): ObjectComparison {
    const added = {};
    const updated = {};
    const removed = {};
    const unchanged = {};
    for (const prop in o1) {
      if (o1.hasOwnProperty(prop)) {
        const o2PropValue = o2[prop];
        const o1PropValue = o1[prop];
        if (o2.hasOwnProperty(prop)) {
          if (o2PropValue === o1PropValue) {
            unchanged[prop] = o1PropValue;
          } else {
            updated[prop] = deep && this.isObject(o1PropValue) && this.isObject(o2PropValue) ? this.diff(o1PropValue, o2PropValue, deep) : {newValue: o2PropValue};
          }
        } else {
          removed[prop] = o1PropValue;
        }
      }
    }
    for (const prop in o2) {
      if (o2.hasOwnProperty(prop)) {
        const o1PropValue = o1[prop];
        const o2PropValue = o2[prop];
        if (o1.hasOwnProperty(prop)) {
          if (o1PropValue !== o2PropValue) {
            if (!deep || !this.isObject(o1PropValue)) {
              updated[prop].oldValue = o1PropValue;
            }
          }
        } else {
          added[prop] = o2PropValue;
        }
      }
    }
    return { added, updated, removed, unchanged };
  }

  /**
   * @return if obj is an Object, including an Array.
   */
  static isObject(obj: any) {
    return obj !== null && typeof obj === 'object';
  }
}

ตัวอย่างการโทร:

ObjectUtils.diff(
  {
    a: 'a', 
    b: 'b', 
    c: 'c', 
    arr: ['A', 'B'], 
    obj: {p1: 'p1', p2: 'p2'}
  },
  {
    b: 'x', 
    c: 'c', 
    arr: ['B', 'C'], 
    obj: {p2: 'p2', p3: 'p3'}, 
    d: 'd'
  },
);

จะกลับมา:

{
  added: {d: 'd'},
  updated: {
    b: {oldValue: 'b', newValue: 'x'},
    arr: {oldValue: ['A', 'B'], newValue: ['B', 'C']},
    obj: {oldValue: {p1: 'p1', p2: 'p2'}, newValue: {p2: 'p2', p3: 'p3'}}
  },
  removed: {a: 'a'},
  unchanged: {c: 'c'},
}

และการเรียกเช่นเดียวกันกับdeepพารามิเตอร์ที่สามจะส่งคืน:

{
  added: {d: 'd'},
  updated: {
    b: {oldValue: 'b', newValue: 'x'},
    arr: {
      added: {},
      removed: {},
      unchanged: {},
      updated: {
        0: {oldValue: 'A', newValue: 'B'},
        1: {oldValue: 'B', newValue: 'C', }
      }
    },
    obj: {
      added: {p3: 'p3'},
      removed: {p1: 'p1'},
      unchanged: {p2: 'p2'},
      updated: {}
    }
  },
  removed: {a: 'a'},
  unchanged: {c: 'c'},
}

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