2008-11-05 11 views
16

मैं दो जावास्क्रिप्ट ऑब्जेक्ट ग्राफ़ों के बीच सभी अंतरों की एक सूची प्राप्त करने में सक्षम होना चाहता हूं, जिसमें संपत्ति नाम और मूल्य जहां डेल्टा होते हैं।मैं दो जावास्क्रिप्ट ऑब्जेक्ट ग्राफ़ के बीच अंतरों की सूची कैसे प्राप्त कर सकता हूं?

यह किस चीज के लायक है, इन ऑब्जेक्ट्स को आमतौर पर सर्वर से JSON के रूप में पुनर्प्राप्त किया जाता है और आम तौर पर परतों की एक मुट्ठी भर से अधिक नहीं होते हैं (यानी यह उन वस्तुओं की एक सरणी हो सकती है जिनके पास डेटा है और फिर अन्य डेटा के साथ सरणी वस्तुओं)।

मैं न केवल बुनियादी गुणों के परिवर्तन देखना चाहते हैं, लेकिन एक सरणी के सदस्यों की संख्या में मतभेद, आदि आदि

अगर मैं एक जवाब नहीं मिलता है, मैं शायद ऊपर लेखन खत्म हो जाएगा यह स्वयं, लेकिन उम्मीद है कि किसी ने पहले से ही यह काम किया है या किसी के बारे में पता है।


संपादित करें: इन वस्तुओं को आम तौर पर एक दूसरे के लिए संरचना में बहुत करीब हो जाएगा, तो हम वस्तुओं है कि एक दूसरे से पूरी तरह अलग हैं के बारे में बात नहीं कर रहे हैं, लेकिन 3 या 4 डेल्टा हो सकता है।

+0

जेसन, धन्यवाद। मैं फिर भी अपना जवाब हटाने जा रहा हूँ। जाहिर है, यह वह नहीं है जिसके बाद आप कहां हैं, और कार्यान्वयन उस पर बहस योग्य है, इसलिए मुझे लगता है कि इसे रखने के लिए कोई मूल्य नहीं है। यदि आपके पास कोई समाधान है जो आपकी आवश्यकताओं के अनुरूप है तो मुझे इसे यहां देखना दिलचस्प लगेगा। – Tomalak

+0

मुझे निराश है कि आपने इसे हटाने का फैसला किया है, लेकिन ऐसा करने के आपके अधिकार का सम्मान करें। मैंने बस सीखा है कि ऐसी चीजें हैं जिन्हें दूसरों के समाधानों के समाधानों से सीखा जा सकता है, भले ही वे उस सटीक समस्या को हल न करें जो उन्हें डिजाइन किया गया था। हम बस कुछ भी सीख सकते हैं। –

उत्तर

12

मौजूदा जवाब समीक्षा करने के बाद, मैंने देखा कि https://github.com/flitbit/diff पुस्तकालय अभी तक एक समाधान के रूप में सूचीबद्ध नहीं किया गया।

मेरे शोध से, यह पुस्तकालय अलग-अलग वस्तुओं की चुनौती को हल करने के लिए सक्रिय विकास, योगदान और कांटे के मामले में सबसे अच्छा प्रतीत होता है। सर्वर सर्वर पर एक diff बनाने और क्लाइंट को केवल बदले गए बिट्स को पास करने के लिए यह बहुत आसान है।

+1

मैंने अपना आधिकारिक उत्तर अपने आप से बदल दिया, क्योंकि यह एक पुराना सवाल है, और 7 साल पहले मैंने इसे लिखने के बाद जावास्क्रिप्ट की दुनिया में काफी बदलाव किया है! :) –

+1

हां, जेएस की दुनिया जंगली ढंग से बदल गई है। मैंने कभी सोचा नहीं कि मैं जावास्क्रिप्ट में बैकएंड प्रोग्रामिंग कर रहा हूं! धन्यवाद, @ जेसनबंटिंग – ryanm

19

यहां मेरी समस्या का आंशिक, भरोसेमंद समाधान है - मैं इसे अद्यतन कर दूंगा क्योंकि मैं इसे और विकसित करता हूं।

function findDifferences(objectA, objectB) { 
    var propertyChanges = []; 
    var objectGraphPath = ["this"]; 
    (function(a, b) { 
     if(a.constructor == Array) { 
     // BIG assumptions here: That both arrays are same length, that 
     // the members of those arrays are _essentially_ the same, and 
     // that those array members are in the same order... 
     for(var i = 0; i < a.length; i++) { 
      objectGraphPath.push("[" + i.toString() + "]"); 
      arguments.callee(a[i], b[i]); 
      objectGraphPath.pop(); 
     } 
     } else if(a.constructor == Object || (a.constructor != Number && 
       a.constructor != String && a.constructor != Date && 
       a.constructor != RegExp && a.constructor != Function && 
       a.constructor != Boolean)) { 
     // we can safely assume that the objects have the 
     // same property lists, else why compare them? 
     for(var property in a) { 
      objectGraphPath.push(("." + property)); 
      if(a[property].constructor != Function) { 
       arguments.callee(a[property], b[property]); 
      } 
      objectGraphPath.pop(); 
     } 
     } else if(a.constructor != Function) { // filter out functions 
     if(a != b) { 
      propertyChanges.push({ "Property": objectGraphPath.join(""), "ObjectA": a, "ObjectB": b }); 
     } 
     } 
    })(objectA, objectB); 
    return propertyChanges; 
} 

और यहाँ यह कैसे उपयोग किया जाएगा और डेटा यह प्रदान करेगा का एक नमूना है (कृपया लंबे उदाहरण बहाना है, लेकिन मैं अपेक्षाकृत गैर तुच्छ कुछ प्रयोग करना चाहते हैं):

var person1 = { 
    FirstName : "John", 
    LastName : "Doh", 
    Age : 30, 
    EMailAddresses : [ 
     "[email protected]", 
     "[email protected]" 
    ], 
    Children : [ 
     { 
     FirstName : "Sara", 
     LastName : "Doe", 
     Age : 2 
     }, { 
     FirstName : "Beth", 
     LastName : "Doe", 
     Age : 5 
     } 
    ] 
}; 

var person2 = { 
    FirstName : "John", 
    LastName : "Doe", 
    Age : 33, 
    EMailAddresses : [ 
     "[email protected]", 
     "[email protected]" 
    ], 
    Children : [ 
     { 
     FirstName : "Sara", 
     LastName : "Doe", 
     Age : 3 
     }, { 
     FirstName : "Bethany", 
     LastName : "Doe", 
     Age : 5 
     } 
    ] 
}; 

var differences = findDifferences(person1, person2); 

Property में

[ 
    { 
     "Property":"this.LastName", 
     "ObjectA":"Doh", 
     "ObjectB":"Doe" 
    }, { 
     "Property":"this.Age", 
     "ObjectA":30, 
     "ObjectB":33 
    }, { 
     "Property":"this.EMailAddresses[1]", 
     "ObjectA":"[email protected]", 
     "ObjectB":"[email protected]" 
    }, { 
     "Property":"this.Children[0].Age", 
     "ObjectA":2, 
     "ObjectB":3 
    }, { 
     "Property":"this.Children[1].FirstName", 
     "ObjectA":"Beth", 
     "ObjectB":"Bethany" 
    } 
] 

this: इस बिंदु पर, यहाँ क्या differences सरणी की तरह अगर आप इसे JSON करने के लिए धारावाहिक लगेगा है मूल्य उस ऑब्जेक्ट की जड़ को संदर्भित करता है जिसकी तुलना की गई थी। तो, यह समाधान अभी तक बिल्कुल नहीं है जो मुझे चाहिए, लेकिन यह बहुत करीब है।

आशा है कि यह किसी के लिए उपयोगी होगा, और यदि आपके पास सुधार के लिए कोई सुझाव है, तो मैं सभी कान हूं; मैंने कल रात बहुत देर से लिखा (यानी आज सुबह) और ऐसी चीजें हो सकती हैं जो मैं पूरी तरह से देख रहा हूं।

धन्यवाद।

+0

यह बहुत अच्छा काम करता है, हालांकि मैं –

+2

दोस्तों के अंतर का एक उद्देश्य बनाना चाहता हूं, कृपया उस पर ध्यान दें: ES5 arguments.callee में सख्त मोड में वर्जित है। https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/callee – BotanMan

4

समाधान 1

उपयोग JSON.stringify (obj) वस्तुओं आप तुलना करना चाहते की एक स्ट्रिंग प्रतिनिधित्व मिलता है। स्ट्रिंग को एक फ़ाइल में सहेजें। टेक्स्ट फ़ाइलों की तुलना करने के लिए किसी भी diff दर्शक का उपयोग करें।

नोट: JSON.stringify फ़ंक्शन परिभाषाओं को इंगित करने वाली गुणों को अनदेखा कर देगा।

समाधान 2

यह वही है आप कुछ संशोधन के साथ चाहते हो सकता है, यह समारोह _.isEqual (http://documentcloud.github.com/underscore/) का एक संशोधित संस्करण है। किसी भी संशोधन का सुझाव देने के लिए स्वतंत्र महसूस करें! मैंने यह पता लगाने के लिए लिखा कि दो वस्तुओं के बीच पहला अंतर कहां होता है।

// Given two objects find the first key or value not matching, algorithm is a 
// inspired by of _.isEqual. 
function diffObjects(a, b) { 
    console.info("---> diffObjects", {"a": a, "b": b}); 
    // Check object identity. 
    if (a === b) return true; 
    // Different types? 
    var atype = typeof(a), btype = typeof(b); 
    if (atype != btype) { 
    console.info("Type mismatch:", {"a": a, "b": b}); 
    return false; 
    }; 
    // Basic equality test (watch out for coercions). 
    if (a == b) return true; 
    // One is falsy and the other truthy. 
    if ((!a && b) || (a && !b)) { 
    console.info("One is falsy and the other truthy:", {"a": a, "b": b}); 
    return false; 
    } 
    // Unwrap any wrapped objects. 
    if (a._chain) a = a._wrapped; 
    if (b._chain) b = b._wrapped; 
    // One of them implements an isEqual()? 
    if (a.isEqual) return a.isEqual(b); 
    // Check dates' integer values. 
    if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime(); 
    // Both are NaN? 
    if (_.isNaN(a) && _.isNaN(b)) { 
    console.info("Both are NaN?:", {"a": a, "b": b}); 
    return false; 
    } 
    // Compare regular expressions. 
    if (_.isRegExp(a) && _.isRegExp(b)) 
    return a.source  === b.source && 
      a.global  === b.global && 
      a.ignoreCase === b.ignoreCase && 
      a.multiline === b.multiline; 
    // If a is not an object by this point, we can't handle it. 
    if (atype !== 'object') { 
    console.info("a is not an object:", {"a": a}); 
    return false; 
    } 
    // Check for different array lengths before comparing contents. 
    if (a.length && (a.length !== b.length)) { 
    console.info("Arrays are of different length:", {"a": a, "b": b}); 
    return false; 
    } 
    // Nothing else worked, deep compare the contents. 
    var aKeys = _.keys(a), bKeys = _.keys(b); 
    // Different object sizes? 
    if (aKeys.length != bKeys.length) { 
    console.info("Different object sizes:", {"a": a, "b": b}); 
    return false; 
    } 
    // Recursive comparison of contents. 
    for (var key in a) if (!(key in b) || !diffObjects(a[key], b[key])) return false; 
    return true; 
}; 
7

एक लाइब्रेरी github.com/NV/objectDiff.js है जो आपको ऐसा करने की अनुमति देती है।demo page पर आप दो जावास्क्रिप्ट ऑब्जेक्ट्स के बीच एक अंतर देख सकते हैं।

3

यदि आप नोडजेएस का उपयोग कर रहे हैं, तो इस स्क्रिप्ट में एनपीएम संस्करण भी है। https://github.com/NV/objectDiff.js

आनंद लें।

+1

भयानक प्लगइन। – Sid

5

आप rus-diff https://github.com/mirek/node-rus-diff भी कोशिश कर सकते हैं जो MongoDB संगत (नाम बदलें/सेट/सेट) diff उत्पन्न करता है।

अपने उदाहरण के लिए वस्तुओं:

var person1 = { 
    FirstName: "John", 
    LastName: "Doh", 
    Age: 30, 
    EMailAddresses: ["[email protected]", "[email protected]"], 
    Children: [ 
    { 
     FirstName: "Sara", 
     LastName: "Doe", 
     Age: 2 
    }, { 
     FirstName: "Beth", 
     LastName: "Doe", 
     Age: 5 
    } 
    ] 
}; 

var person2 = { 
    FirstName: "John", 
    LastName: "Doe", 
    Age: 33, 
    EMailAddresses: ["[email protected]", "[email protected]"], 
    Children: [ 
    { 
     FirstName: "Sara", 
     LastName: "Doe", 
     Age: 3 
    }, { 
     FirstName: "Bethany", 
     LastName: "Doe", 
     Age: 5 
    } 
    ] 
}; 

var rusDiff = require('rus-diff').rusDiff 

console.log(rusDiff(person1, person2)) 

यह सेट की एक सूची उत्पन्न:

{ '$set': 
    { 'Age': 33, 
    'Children.0.Age': 3, 
    'Children.1.FirstName': 'Bethany', 
    'EMailAddresses.1': '[email protected]', 
    'LastName': 'Doe' } } 
1

मैं हाल ही में यह करने के लिए एक मॉड्यूल लिखा था, क्योंकि मैं कई diffing मॉड्यूल से संतुष्ट नहीं था मैंने पाया (मैंने सबसे लोकप्रिय मॉड्यूल का एक गुच्छा सूचीबद्ध किया है और वे मेरे मॉड्यूल के रीडेमे में स्वीकार्य क्यों नहीं थे)। इसे odiff कहा जाता है: https://github.com/Tixit/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}]} 
] 
*/ 
0

मुझे मिली पुस्तकालयों में से कोई भी पर्याप्त नहीं था इसलिए मैंने अपना स्वयं का AngularJS कारखाना लिखा। यह वस्तुओं को दोनों तरीकों से तुलना करता है और केवल उसी संरचना के भीतर अंतर देता है। अपने समर्थन के लिए

/** 
* Diff 
* Original author: Danny Coulombe 
* Creation date: 2016-05-18 
* 
* Work with objects to find their differences. 
*/ 
controllers.factory('diff', [function() { 

    var factory = { 

     /** 
     * Compare the original object with the modified one and return their differences. 
     * 
     * @param original: Object 
     * @param modified: Object 
     * 
     * @return Object 
     */ 
     getDifferences: function(original, modified) { 

      var type = modified.constructor === Array ? [] : {}; 
      var result = angular.copy(type); 
      var comparisons = [[original, modified, 1], [modified, original, 0]]; 

      comparisons.forEach(function(comparison) { 

       angular.forEach(comparison[0], function(value, key) { 

        if(result[key] === undefined) { 

         if(comparison[1][key] !== undefined && value !== null && comparison[1][key] !== null && [Object, Array].indexOf(comparison[1][key].constructor) !== -1) { 

          result[key] = factory.getDifferences(value, comparison[1][key]); 
         } 
         else if(comparison[1][key] !== value) { 

          result[key] = comparison[comparison[2]][key]; 
         } 

         if(angular.equals(type, result[key]) 
         || result[key] === undefined 
         || (
          comparison[0][key] !== undefined 
          && result[key] !== null 
          && comparison[0][key] !== null 
          && comparison[0][key].length === comparison[1][key].length 
          && result[key].length === 0 
         )) { 
          delete result[key]; 
         } 
        } 
       }); 
      }); 

      return result; 
     } 
    }; 

    return factory; 
}]); 
+0

जब मैं इसे चलाता हूं तो मुझे "स्रोत अपरिभाषित" मिलता है। क्या "मूल" "स्रोत" होना चाहिए? इसके अलावा, "fromObjects" कहां परिभाषित किया गया है? – adam0101

+0

क्षमा करें, मैंने इस उत्तर को लिखते समय कुछ परिवर्तनीय नाम बदल दिए हैं। "स्रोत" "मूल" था और "fromObjects" "getDifferences" था। मैंने उपरोक्त स्क्रिप्ट को अपडेट किया है। –

0

var d = { 
 
    FirstName : "John", 
 
    LastName : "Doh", 
 
    Age : 30, 
 
    EMailAddresses : [ 
 
     "[email protected]", 
 
     "[email protected]" 
 
    ], 
 
    Children : [ 
 
     { 
 
     FirstName : "Sara", 
 
     LastName : "Doe", 
 
     Age : 2 
 
     }, { 
 
     FirstName : "Beth", 
 
     LastName : "Doe", 
 
     Age : 5 
 
     } 
 
    ] 
 
}; 
 

 
var f = { 
 
    FirstName : "John", 
 
    LastName : "Doe", 
 
    Age : 33, 
 
    EMailAddresses : [ 
 
     "[email protected]", 
 
     "[email protected]" 
 
    ], 
 
    Children : [ 
 
     { 
 
     FirstName : "Sara", 
 
     LastName : "Doe", 
 
     Age : 3 
 
     }, { 
 
     FirstName : "Bethany", 
 
     LastName : "Doe", 
 
     Age : 5 
 
     } 
 
    ] 
 
}; 
 

 
resultobj = [] 
 
function comp_obj(t1,t2){ 
 
\t flag = 1; 
 
\t key1_arr = Object.keys(t1) 
 
\t key2_arr = Object.keys(t2) 
 
\t if(key1_arr.length == key2_arr.length){ 
 
\t \t for(key1 in t1){ 
 
\t \t \t ty1 = Object.prototype.toString.call(t1[key1]) 
 
\t \t \t ty2 = Object.prototype.toString.call(t2[key1]) 
 
\t \t \t if(ty1 == ty2) { 
 
\t \t \t \t if(ty1 == '[object String]' || ty1 == '[object Number]'){ 
 
\t \t \t \t \t if(t2[key1] != t1[key1]){ 
 
\t \t \t \t \t \t flag = 0; 
 
\t \t \t \t \t \t break; 
 
\t \t \t \t \t } \t 
 
\t \t \t \t }else if(ty1 == '[object Array]'){ 
 
\t \t \t \t \t var result = comp_arr(t1[key1],t2[key1]); 
 
\t \t \t \t \t console.log(ty1,ty2) 
 
\t \t \t \t \t if(!result) 
 
\t \t \t \t \t \t flag = 0; 
 
\t \t \t \t }else if(ty1 == '[object Object]'){ 
 
\t \t \t \t \t var result = comp_obj(t1[key1],t2[key1]) 
 
\t \t \t \t \t if(!result) 
 
\t \t \t \t \t \t flag = 0; 
 
\t \t \t \t \t \t 
 
\t \t \t \t } 
 
\t \t \t }else{ 
 
\t \t \t \t flag = 0; 
 
\t \t \t \t break; \t 
 
\t \t \t } 
 

 
\t \t } 
 
\t }else{ 
 
\t \t flag \t = 0; 
 
\t } 
 
\t if(flag) 
 
\t \t return true 
 
\t else 
 
\t \t return false; 
 
} 
 
function comp_arr(a,b){ 
 
\t flag = 1; 
 
\t if(a.length == b.length){ 
 
\t \t for(var i=0,l=a.length;i<l;i++){ 
 
\t \t \t type1 = Object.prototype.toString.call(a[i]) 
 
\t \t \t type2 = Object.prototype.toString.call(b[i]) 
 
\t \t \t if(type1 == type2) { 
 
\t \t \t \t if(type1 == '[object String]' || type1 == '[object Number]'){ 
 
\t \t \t \t \t if(a[i] != b[i]){ 
 
\t \t \t \t \t \t flag = 0; 
 
\t \t \t \t \t \t break; 
 
\t \t \t \t \t } \t 
 
\t \t \t \t }else if(type1 == '[object Array]'){ 
 
\t \t \t \t \t var result = comp_arr(a[i],b[i]); 
 
\t \t \t \t \t if(!result) 
 
\t \t \t \t \t \t flag = 0; 
 
\t \t \t \t }else if(type1 == '[object Object]'){ 
 
\t \t \t \t \t var result = comp_obj(a[i],b[i]) 
 
\t \t \t \t \t if(!result) 
 
\t \t \t \t \t \t flag = 0; 
 
\t \t \t \t } \t 
 
\t \t \t }else{ 
 
\t \t \t \t flag = 0; 
 
\t \t \t \t break; \t 
 
\t \t \t } 
 
\t \t } 
 
\t }else 
 
\t \t flag = 0; 
 
\t if(flag) 
 
\t \t return true 
 
\t else 
 
\t \t return false; 
 
} 
 
function create(t,attr,parent_node,innerdata){ 
 
\t var dom = document.createElement(t) 
 
\t for(key in attr){ 
 
\t \t dom.setAttribute(key,attr[key]) 
 
\t } 
 
\t dom.innerHTML = innerdata; 
 
\t parent_node.appendChild(dom) 
 
\t return dom; 
 
} 
 
window.onload = function() { 
 
\t for(key in f){ 
 
\t \t if(!(key in d)) 
 
\t \t \t resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])}) 
 
\t \t type1 = Object.prototype.toString.call(f[key]) 
 
\t \t type2 = Object.prototype.toString.call(d[key]) 
 
\t \t if(type1 == type2){ 
 
\t \t \t if(type1 == '[object String]' || type1 == '[object Number]'){ 
 
\t \t \t \t if(f[key] != d[key]) 
 
\t \t \t \t \t resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])}) 
 
\t \t \t }else if(type1 == '[object Array]'){ 
 
\t \t \t \t var result = comp_arr(f[key],d[key]); 
 
\t \t \t \t if(!result) 
 
\t \t \t \t \t resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])}) 
 
\t \t \t }else if(type1 == '[object Object]'){ 
 
\t \t \t \t var result = comp_obj(f[key],d[key]) \t 
 
\t \t \t \t if(!result) 
 
\t \t \t \t \t resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])}) 
 
\t \t \t } 
 
\t \t }else 
 
\t \t \t resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])}) 
 
\t } 
 
\t var tb = document.getElementById('diff'); 
 
\t var s1 = document.getElementById('source1'); 
 
\t var s2 = document.getElementById('source2'); 
 
\t s1.innerHTML = 'Object 1 :'+ JSON.stringify(f) 
 
\t s2.innerHTML = 'Object 2 :'+JSON.stringify(d) 
 
\t resultobj.forEach(function(data,i){ 
 
\t \t \t tr_dom = create('tr',{},tb,'') 
 
\t \t \t no = create('td',{},tr_dom,i+1) 
 
\t \t \t Object.keys(data).forEach(function(tr){ 
 
\t \t \t \t td_dom = create('td',{},tr_dom,data[tr]) 
 
\t \t \t }) 
 
\t }) 
 
}
<html> 
 
     <body> 
 
       <p id="source1"> </p> 
 
       <p id="source2"> </p> 
 
       <p id="source7"> DIFFERENCE TABLE</p> 
 
       <table border=''> 
 
         <thead> 
 
           <th>S.no</th> 
 
           <th>Name Of the Key</th> 
 
           <th>Object1 Value</th> 
 
           <th>Object2 Value</th> 
 
         </thead> 
 
         <tbody id="diff"> 
 

 
         </tbody> 
 
       </table> 
 
      </body> 
 
</html>

संबंधित मुद्दे

 संबंधित मुद्दे