दो वस्तुओं के बीच सामान्य गहरा अंतर


222

मेरे पास दो वस्तुएं हैं: oldObjऔर newObj

oldObjकिसी प्रपत्र को पॉप्युलेट करने के लिए डेटा का उपयोग किया गया था और newObjयह इस रूप में उपयोगकर्ता के डेटा को बदलने और सबमिट करने का परिणाम है।

दोनों वस्तुएं गहरी हैं, अर्थात। उनके पास ऐसे गुण हैं जो ऑब्जेक्ट्स या ऑब्जेक्ट्स के एरेज़ आदि हैं - वे एन लेवल डीप हो सकते हैं, इस प्रकार अलग एल्गोरिथ्म को पुनरावर्ती होने की आवश्यकता होती है।

अब मैं सिर्फ (में के रूप में जोड़ा / अद्यतन / हटाए गए) को समझ नहीं करने के लिए क्या बदल गया था से जरूरत 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], जो कि स्ट्रिंग, इंट & बूल जैसे मूल्य आधारित प्रकारों के सरणियों के लिए पर्याप्त सरल है, लेकिन जब यह आता है तो संभालना वास्तव में मुश्किल हो जाता है वस्तुओं और सरणियों जैसे संदर्भ प्रकारों के सरणियाँ।

उदाहरण सरणियाँ जो समान पाई जानी चाहिए:

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

न केवल इस प्रकार के गहरे मूल्य समानता के लिए जांच करना काफी जटिल है, बल्कि उन परिवर्तनों का प्रतिनिधित्व करने के लिए एक अच्छा तरीका भी पता लगाना है जो हो सकते हैं।



2
@ a'r: यह stackoverflow.com/questions/1200562/… का डुप्लिकेट नहीं है - मुझे पता है कि वस्तुओं को कैसे निकालना है , मैं पूर्व कला की तलाश कर रहा हूं क्योंकि यह गैर तुच्छ है और इसे लागू करने के लिए वास्तविक समय लगेगा, और मैं इसके बजाय खरोंच से इसे बनाने के लिए एक पुस्तकालय का उपयोग करें।
मार्टिन जेस्परसन

1
क्या आपको वास्तव में वस्तुओं के अलग-अलग होने की आवश्यकता है, क्या फॉर्म से प्रतिक्रिया प्रस्तुत करने पर सर्वर से उत्पन्न newObj है? क्योंकि यदि आपके पास किसी ऑब्जेक्ट का "सर्वर अपडेट" नहीं है, तो आप उचित ईवेंट श्रोताओं और उपयोगकर्ता इंटरैक्शन (ऑब्जेक्ट परिवर्तन) को संलग्न करके अपनी समस्या को आसान बना सकते हैं जो आप चाहते थे कि परिवर्तन सूची को अपडेट / जनरेट कर सकें।
sbgoran

1
@sbgoran: newObjDOM में एक फॉर्म से js कोड रीडिंग वैल्यूज द्वारा जेनरेट किया जाता है। राज्य को बनाए रखने और इसे बहुत आसान करने के कई तरीके हैं, लेकिन मैं इसे एक अभ्यास के रूप में स्टेटलेस रखना चाहूंगा। इसके अलावा, मैं यह देखने के लिए पूर्व कला की तलाश कर रहा हूं कि दूसरों ने इससे कैसे निपट लिया है, अगर वास्तव में किसी के पास है।
मार्टिन जेस्पर्सन

3
यहाँ अंतर करने के लिए एक बहुत ही परिष्कृत पुस्तकालय है / जावास्क्रिप्ट वस्तुओं के किसी भी जोड़े पैच github.com/benjamine/jsondiffpatch आप देख सकते हैं इसे यहाँ रहते हैं: benjamine.github.io/jsondiffpatch/demo/index.html (अस्वीकरण: मैं लेखक हूँ)
बेन्जा

जवाबों:


141

मैंने एक छोटी सी कक्षा लिखी जो आप चाहते हैं, आप इसे यहाँ पर परख सकते हैं

केवल एक चीज जो आपके प्रस्ताव से अलग है, वह यह है कि मैं [1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]एक ही नहीं मानता , क्योंकि मुझे लगता है कि सरणियां समान नहीं हैं यदि उनके तत्वों का क्रम समान नहीं है। बेशक जरूरत पड़ने पर इसे बदला जा सकता है। साथ ही फ़ंक्शन को लेने के लिए इस कोड को और बढ़ाया जा सकता है क्योंकि पास किए गए आदिम मूल्यों के आधार पर मनमाने तरीके से ऑब्जेक्ट को प्रारूपित करने के लिए उपयोग किया जाएगा (अब यह काम "तुलनात्मक विधि" द्वारा किया जाता है)।

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)बेकार है क्योंकि सरणियाँ ऐसी वस्तुएं हैं जो सरणियों के उदाहरण हैं।
मार्टिन जेस्पर्सन

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
मैं आपसे पिछले दृष्टिकोण से सहमत हूं - मूल डेटा संरचना को उस चीज़ में बदलना चाहिए जो वास्तविक रूप से करना आसान है। बधाई हो, आपने इसे :) :)
मार्टिन जेस्पर्सन

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}

यह एक गहरे अंतर के लिए अलग होगा:

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

जैसा कि @ जुहाना द्वारा टिप्पणियों में कहा गया है, उपरोक्त केवल एक अंतर है -> बी और प्रतिवर्ती नहीं है (जिसका अर्थ है कि बी में अतिरिक्त गुणों को अनदेखा किया जाएगा)। इसके बजाय -> 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/ देखें


निश्चित नहीं है कि आप नीचे क्यों गए, यह पर्याप्त था क्योंकि आपने एक उथले, सरल उदाहरण के साथ-साथ एक अधिक जटिल गहन कार्य प्रदान किया था।
सियारिया

2
@ सियारिया नफरत करने वाले हैं, मुझे लगता है ... मैंने दोनों किया क्योंकि मैं मूल रूप से सोचा था omitकि एक गहरा अंतर होगा, लेकिन गलत था, इसलिए तुलना के लिए भी शामिल था।
drzaus

1
अच्छा समाधान। मैं बदलने के लिए सुझाव है कि r[k] = ... : vमें r[k] = ... : {'a':v, 'b':b[k] }इस तरह से आप दो मानों देख सकते हैं।
मंगलोनी 17

2
जब ऑब्जेक्ट अन्यथा समान होते हैं तो ये दोनों एक गलत नकारात्मक लौटाते हैं लेकिन दूसरे में अधिक तत्व होते हैं, जैसे {a:1, b:2}और {a:1, b:2, c:3}
जेजे

1
इसके _.omitByबजाय होना चाहिए _.omit
जेपी

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 ...
सीनो

2
मुझे समाधान पसंद है लेकिन इसकी एक समस्या है, यदि वस्तु एक स्तर से अधिक गहरी है, तो यह बदली हुई नेस्टेड वस्तुओं में सभी मूल्यों को वापस कर देगी - या कम से कम मेरे लिए यही हो रहा है।
नकली

3
हाँ, यह पुनरावर्ती @Spurious नहीं है
नेमेसेरियल

2
बस ध्यान रखें कि इस समाधान के साथ, ऑब्जेक्ट में प्रत्येक तत्व के लिए आपको एक पूरी तरह से नई वस्तु मिलती है जो सभी मौजूदा तत्वों के साथ बनाई गई है जो कि केवल एक आइटम को सरणी में जोड़ने के लिए इसमें कॉपी की गई है। छोटी वस्तुओं के लिए यह ठीक है, लेकिन यह बड़ी वस्तुओं के लिए तेजी से धीमा हो जाएगा।
मालवणी

22

लोडश का उपयोग करना:

_.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 में परिवर्तित किया गया। इस बदलाव को नोटिस करने के लिए धन्यवाद एरीओन।


6
लंकाशर 4.15.0 _.merge के साथ customizer फ़ंक्शन अब समर्थित नहीं है, इसलिए आपको इसके बजाय _.mergeWith का उपयोग करना चाहिए।
अविरन कोहेन

1
यह फ़ंक्शन बहुत अच्छा है लेकिन नेस्टेड ऑब्जेक्ट में काम नहीं कर रहा है।
जो एलन

13

यहाँ एक जावास्क्रिप्ट लाइब्रेरी है जिसे आप दो जावास्क्रिप्ट ऑब्जेक्ट्स के बीच अंतर खोजने के लिए उपयोग कर सकते हैं:

Github URL: 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 में आपको 'पुनरावर्ती-भिन्न' मॉड्यूल की आवश्यकता हो सकती है और इसे नीचे की तरह उपयोग कर सकते हैं:

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

यह उदाहरण के लिए, दिनांक गुणों में परिवर्तन के लिए जिम्मेदार नहीं होगा।
ट्रोलकॉज़ेव

तारीख समर्थन जोड़ा गया है
अनंत

9

इन दिनों, इसके लिए काफी कुछ मॉड्यूल उपलब्ध हैं। मैंने हाल ही में ऐसा करने के लिए एक मॉड्यूल लिखा था, क्योंकि मैं अपने द्वारा पाए गए कई अलग-अलग मॉड्यूल से संतुष्ट नहीं था। इसका नाम odiff: https://github.com/Tixit/odiff है । मैंने सबसे लोकप्रिय मॉड्यूलों का एक समूह भी सूचीबद्ध किया है और वे क्यों नहीं पढ़ रहे हैं 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);

500k से अधिक साप्ताहिक डाउनलोड के साथ एक npm मॉड्यूल है: 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
मैं यहां
एक्सट्रीम

ऑब्जेक्ट्स को मर्ज करना तुच्छ है और इसे $.extend(true,obj1,obj2)jQuery का उपयोग करना जितना आसान हो सकता है । यह बिल्कुल नहीं है जो मुझे चाहिए। मुझे उन दोनों वस्तुओं के बीच अंतर की आवश्यकता है, न कि उनके संयोजन की।
मार्टिन जेस्पर्सन

इसके महान कि एक्सट का उपयोग यहां किया जाता है
पेरोक्साइड

2

मैंने जावास्क्रिप्ट में "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

मुझे पता है कि मुझे पार्टी में देर हो रही है, लेकिन मुझे कुछ इसी तरह की ज़रूरत थी कि उपरोक्त उत्तर मदद नहीं करते थे।

मैं एक चर में परिवर्तन का पता लगाने के लिए कोणीय के $ घड़ी समारोह का उपयोग कर रहा था। न केवल मुझे यह जानने की आवश्यकता है कि क्या कोई संपत्ति चर पर बदल गई थी, लेकिन मैं यह भी सुनिश्चित करना चाहता था कि जो संपत्ति बदली गई थी वह अस्थायी, गणना की गई फ़ील्ड नहीं थी। दूसरे शब्दों में, मैं कुछ गुणों को अनदेखा करना चाहता था।

यहाँ कोड है: 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

ऐसा लगता है कि डिफाइनरप्रॉर्टी बेहतर प्रदर्शन के साथ इस समस्या को हल करेगा, अगर मुझे सही ढंग से याद है कि यह IE9 के लिए सभी तरह से काम करता है।
पीटर

धन्यवाद..!! आपका कोड आकर्षण की तरह काम करता है और मेरे दिन को बचाता है। मेरे पास 1250 लाइनों की json ऑब्जेक्ट है और यह मुझे सटीक o / p देता है जो मुझे चाहिए।
तेजस मेहता

1

मैं सिर्फ रम्दा का उपयोग करता हूं, उसी समस्या को हल करने के लिए, मुझे यह जानना होगा कि नई वस्तु में क्या बदला है। तो यहाँ मेरा डिजाइन।

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

यहाँ @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

यहाँ गिसथब पर पाए जाने वाले किसी चीज़ का संशोधित संस्करण है ।

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 के उत्तर को संशोधित किया ताकि परिणामी भिन्न वस्तु में केवल बदले हुए मान शामिल हों , और उन मानों को छोड़ देता है जो समान थे। इसके अलावा, यह मूल मूल्य और अद्यतन मूल्य दोनों को दर्शाता है ।

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

मैं दो वस्तुओं के बीच अंतर पाने के लिए एक रास्ता खोजने की कोशिश कर रहा था। लॉडश का उपयोग करके यह मेरा समाधान है:

// 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 द्वारा ऊपर दिए गए उत्तर को लिया और इसे अपने केस के लिए संशोधित किया, जैसा कि प्रश्न की जरूरत है, सेट के रूप में सरणियों का इलाज करने के लिए (यानी ऑर्डर अंतर के लिए महत्वपूर्ण नहीं है)

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

यहाँ एक समाधान है कि:

  • टाइपस्क्रिप्ट (लेकिन जावास्क्रिप्ट के लिए आसानी से परिवर्तनीय)
  • कोई निर्भरता नहीं है
  • जेनेरिक, और ऑब्जेक्ट प्रकारों की जाँच के बारे में परवाह नहीं करता (एक तरफ 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'},
}

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.