如何获取两个JavaScript对象图之间的差异列表?

时间:2021-05-21 21:32:21

I want to be able to get a list of all differences between two JavaScript object graphs, with the property names and values where the deltas occur.

我希望能够获得两个JavaScript对象图之间所有差异的列表,以及发生增量的属性名称和值。

For what it is worth, these objects are usually retrieved from the server as JSON and typically are no more than a handful of layers deep (i.e. it may be an array of objects that themselves have data and then arrays with other data objects).

对于它的价值,这些对象通常作为JSON从服务器检索,并且通常只有少数深层(即它可能是一个对象数组,它们本身具有数据,然后是与其他数据对象一起的数组)。

I want to not only see the changes to basic properties, but differences in the number of members of an array, etc. etc.

我想不仅要看到基本属性的更改,还要看到数组成员数量的差异等。

If I don't get an answer, I will probably end up writing this myself, but hope someone has already done this work or know of someone who has.

如果我没有得到答案,我可能最终会自己写这篇文章,但希望有人已经完成了这项工作或者知道有人。


EDIT: These objects will typically be very close in structure to one another, so we are not talking about objects that are utterly different from one another, but may have 3 or 4 deltas.

编辑:这些对象通常在结构上彼此非常接近,所以我们不是在谈论彼此完全不同的对象,而是可能有3或4个增量。

9 个解决方案

#1


13  

After reviewing the existing answers, I noticed that the https://github.com/flitbit/diff library was not yet listed as a solution.

在查看现有答案后,我注意到https://github.com/flitbit/diff库尚未列为解决方案。

From my research, this library seems to be the best in terms of active development, contributions and forks for solving the challenge of diffing objects. This is very handy for creating a diff on the server side and passing the client only the changed bits.

根据我的研究,这个库在积极开发,贡献和分叉方面似乎是最好的,可以解决差异对象的挑战。这对于在服务器端创建diff并仅向客户端传递更改的位非常方便。

#2


19  

Here is a partial, naïve solution to my problem - I will update this as I further develop it.

这是我的问题的一个部分,天真的解决方案 - 我将在我进一步开发它时更新它。

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

And here is a sample of how it would be used and the data it would provide (please excuse the long example, but I want to use something relatively non-trivial):

这里有一个如何使用它和它将提供的数据的样本(请原谅长的例子,但我想使用一些相对非平凡的东西):

var person1 = { 
   FirstName : "John", 
   LastName : "Doh", 
   Age : 30, 
   EMailAddresses : [
      "john.doe@gmail.com", 
      "jd@initials.com"
   ], 
   Children : [ 
      { 
         FirstName : "Sara", 
         LastName : "Doe", 
         Age : 2 
      }, { 
         FirstName : "Beth", 
         LastName : "Doe", 
         Age : 5 
      } 
   ] 
};

var person2 = { 
   FirstName : "John", 
   LastName : "Doe", 
   Age : 33, 
   EMailAddresses : [
      "john.doe@gmail.com", 
      "jdoe@hotmail.com"
   ], 
   Children : [ 
      { 
         FirstName : "Sara", 
         LastName : "Doe", 
         Age : 3 
      }, { 
         FirstName : "Bethany", 
         LastName : "Doe", 
         Age : 5 
      } 
   ] 
};

var differences = findDifferences(person1, person2);

At this point, here is what the differences array would look like if you serialized it to JSON:

此时,如果将序列化为JSON,则以下是差异数组的样子:

[
   {
      "Property":"this.LastName", 
      "ObjectA":"Doh", 
      "ObjectB":"Doe"
   }, {
      "Property":"this.Age", 
      "ObjectA":30, 
      "ObjectB":33
   }, {
      "Property":"this.EMailAddresses[1]", 
      "ObjectA":"jd@initials.com", 
      "ObjectB":"jdoe@hotmail.com"
   }, {
      "Property":"this.Children[0].Age", 
      "ObjectA":2, 
      "ObjectB":3
   }, {
      "Property":"this.Children[1].FirstName", 
      "ObjectA":"Beth", 
      "ObjectB":"Bethany"
   }
]

The this in the Property value refers to the root of the object that was compared. So, this solution is not yet exactly what I need, but it is pretty darn close.

Property属性中的this指的是被比较对象的根。所以,这个解决方案还不是我需要的,但它非常接近。

Hope this is useful to someone out there, and if you have any suggestions for improvement, I am all-ears; I wrote this very late last night (i.e. early this morning) and there may be things I am completely overlooking.

希望这对那里的人有用,如果你有任何改进的建议,我全心全意;我昨晚很晚才写到这个(即今天凌晨),我可能会完全忽视这些事情。

Thanks.

谢谢。

#3


8  

There is an objectDiff library which allows you to do that. On its demo page you can see a difference between two javascript objects.

有一个objectDiff库,允许您这样做。在其演示页面上,您可以看到两个javascript对象之间的差异。

#4


5  

You can also try rus-diff https://github.com/mirek/node-rus-diff which generates MongoDB compatible (rename/unset/set) diff.

您还可以尝试使用rus-diff https://github.com/mirek/node-rus-diff生成兼容MongoDB(重命名/取消设置/设置)差异。

For your example objects:

对于您的示例对象:

var person1 = {
  FirstName: "John",
  LastName: "Doh",
  Age: 30,
  EMailAddresses: ["john.doe@gmail.com", "jd@initials.com"],
  Children: [
    {
      FirstName: "Sara",
      LastName: "Doe",
      Age: 2
    }, {
      FirstName: "Beth",
      LastName: "Doe",
      Age: 5
    }
  ]
};

var person2 = {
  FirstName: "John",
  LastName: "Doe",
  Age: 33,
  EMailAddresses: ["john.doe@gmail.com", "jdoe@hotmail.com"],
  Children: [
    {
      FirstName: "Sara",
      LastName: "Doe",
      Age: 3
    }, {
      FirstName: "Bethany",
      LastName: "Doe",
      Age: 5
    }
  ]
};

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

console.log(rusDiff(person1, person2))

It generates a list of sets:

它生成一组集:

{ '$set': 
   { 'Age': 33,
     'Children.0.Age': 3,
     'Children.1.FirstName': 'Bethany',
     'EMailAddresses.1': 'jdoe@hotmail.com',
     'LastName': 'Doe' } }

#5


4  

Solution 1

解决方案1

Use JSON.stringify(obj) to get a string representation of the objects you want to compare. Save the string to a file. Use any diff viewer to compare the text files.

使用JSON.stringify(obj)获取要比较的对象的字符串表示形式。将字符串保存到文件。使用任何diff查看器来比较文本文件。

Note: JSON.stringify will ignore properties that point to function definitions.

注意:JSON.stringify将忽略指向函数定义的属性。

Solution 2

解决方案2

This could do what you want with some modification, it is a modified version of the function _.isEqual (http://documentcloud.github.com/underscore/). Please feel free to suggest any modifications! I wrote it to figure out where the first difference between two objects occur.

这可以通过一些修改来做你想要的,它是函数_.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;
};

#6


3  

This script has a NPM version as well, if you are using NodeJS. https://github.com/NV/objectDiff.js

如果您使用NodeJS,此脚本也具有NPM版本。 https://github.com/NV/objectDiff.js

Rejoice.

麾。

#7


1  

I recently wrote a module to do this, because I wasn't satisfied with the numerous diffing modules I found (I listed a bunch of the most popular modules and why they weren't acceptable in the readme of my module). Its called odiff: https://github.com/Tixit/odiff . Here's an example:

我最近写了一个模块来做这个,因为我对我发现的众多差异模块不满意(我列出了一些最流行的模块以及为什么它们在我的模块的自述文件中是不可接受的)。它叫做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}]}
]
*/

#8


0  

None of the libraries I found were sufficient so I wrote my own AngularJS factory. It compares the objects in both ways and returns only the difference within the same structure.

我找到的所有图书馆都不够,所以我写了自己的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;
}]);

#9


0  

var d = { 
   FirstName : "John", 
   LastName : "Doh", 
   Age : 30, 
   EMailAddresses : [
      "john.doe@gmail.com", 
      "jd@initials.com"
   ], 
   Children : [ 
      { 
         FirstName : "Sara", 
         LastName : "Doe", 
         Age : 2 
      }, { 
         FirstName : "Beth", 
         LastName : "Doe", 
         Age : 5 
      } 
   ] 
};

var f = { 
   FirstName : "John", 
   LastName : "Doe", 
   Age : 33, 
   EMailAddresses : [
      "john.doe@gmail.com", 
      "jdoe@hotmail.com"
   ], 
   Children : [ 
      { 
         FirstName : "Sara", 
         LastName : "Doe", 
         Age : 3 
      }, { 
         FirstName : "Bethany", 
         LastName : "Doe", 
         Age : 5 
      } 
   ] 
};

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

		}
	}else{
		flag	= 0;
	}
	if(flag)
		return true
	else
		return false;
}
function comp_arr(a,b){
	flag = 1;
	if(a.length == b.length ){
		for(var i=0,l=a.length;i<l;i++){
			type1    = Object.prototype.toString.call(a[i])
			type2    = Object.prototype.toString.call(b[i])
			if(type1 == type2) {
				if(type1 == '[object String]' || type1 == '[object Number]' ){
					if( a[i] != b[i]){
						flag = 0;
						break;
					}	
				}else if(type1 == '[object Array]'){
					var result = comp_arr(a[i],b[i]);
					if(!result)
						flag = 0;
				}else if(type1  == '[object Object]'){
					var result = comp_obj(a[i],b[i])
					if(!result)
						flag = 0;
				}	
			}else{
				flag = 0;
				break;	
			}
		}
	}else
		 flag = 0;
	if(flag)
		return true
	else
		return false;
}
function create(t,attr,parent_node,innerdata){
	var dom = document.createElement(t)
	for(key in attr){
		dom.setAttribute(key,attr[key])
	}
	dom.innerHTML = innerdata;
	parent_node.appendChild(dom)
	return dom;
}
window.onload = function () {
	for(key in f){
		if(!(key in d)) 
			resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
		type1    = Object.prototype.toString.call(f[key])
		type2    = Object.prototype.toString.call(d[key])
		if(type1 == type2){
			if(type1 == '[object String]' || type1 == '[object Number]' ){
				if(f[key] != d[key])
					resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
			}else if(type1 == '[object Array]'){
				var result = comp_arr(f[key],d[key]);
				if(!result)
					resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
			}else if(type1 == '[object Object]'){
				var result = comp_obj(f[key],d[key])	
				if(!result)
					resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
			}
		}else 
			resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
	}
	var tb = document.getElementById('diff');
	var s1 = document.getElementById('source1');
	var s2 = document.getElementById('source2');
	s1.innerHTML = 'Object 1 :'+ JSON.stringify(f)
	s2.innerHTML = 'Object 2 :'+JSON.stringify(d)
	resultobj.forEach(function(data,i){
			tr_dom = create('tr',{},tb,'')
			no = create('td',{},tr_dom,i+1)
			Object.keys(data).forEach(function(tr){
				td_dom = create('td',{},tr_dom,data[tr])
			})
	})
}
<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>

#1


13  

After reviewing the existing answers, I noticed that the https://github.com/flitbit/diff library was not yet listed as a solution.

在查看现有答案后,我注意到https://github.com/flitbit/diff库尚未列为解决方案。

From my research, this library seems to be the best in terms of active development, contributions and forks for solving the challenge of diffing objects. This is very handy for creating a diff on the server side and passing the client only the changed bits.

根据我的研究,这个库在积极开发,贡献和分叉方面似乎是最好的,可以解决差异对象的挑战。这对于在服务器端创建diff并仅向客户端传递更改的位非常方便。

#2


19  

Here is a partial, naïve solution to my problem - I will update this as I further develop it.

这是我的问题的一个部分,天真的解决方案 - 我将在我进一步开发它时更新它。

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

And here is a sample of how it would be used and the data it would provide (please excuse the long example, but I want to use something relatively non-trivial):

这里有一个如何使用它和它将提供的数据的样本(请原谅长的例子,但我想使用一些相对非平凡的东西):

var person1 = { 
   FirstName : "John", 
   LastName : "Doh", 
   Age : 30, 
   EMailAddresses : [
      "john.doe@gmail.com", 
      "jd@initials.com"
   ], 
   Children : [ 
      { 
         FirstName : "Sara", 
         LastName : "Doe", 
         Age : 2 
      }, { 
         FirstName : "Beth", 
         LastName : "Doe", 
         Age : 5 
      } 
   ] 
};

var person2 = { 
   FirstName : "John", 
   LastName : "Doe", 
   Age : 33, 
   EMailAddresses : [
      "john.doe@gmail.com", 
      "jdoe@hotmail.com"
   ], 
   Children : [ 
      { 
         FirstName : "Sara", 
         LastName : "Doe", 
         Age : 3 
      }, { 
         FirstName : "Bethany", 
         LastName : "Doe", 
         Age : 5 
      } 
   ] 
};

var differences = findDifferences(person1, person2);

At this point, here is what the differences array would look like if you serialized it to JSON:

此时,如果将序列化为JSON,则以下是差异数组的样子:

[
   {
      "Property":"this.LastName", 
      "ObjectA":"Doh", 
      "ObjectB":"Doe"
   }, {
      "Property":"this.Age", 
      "ObjectA":30, 
      "ObjectB":33
   }, {
      "Property":"this.EMailAddresses[1]", 
      "ObjectA":"jd@initials.com", 
      "ObjectB":"jdoe@hotmail.com"
   }, {
      "Property":"this.Children[0].Age", 
      "ObjectA":2, 
      "ObjectB":3
   }, {
      "Property":"this.Children[1].FirstName", 
      "ObjectA":"Beth", 
      "ObjectB":"Bethany"
   }
]

The this in the Property value refers to the root of the object that was compared. So, this solution is not yet exactly what I need, but it is pretty darn close.

Property属性中的this指的是被比较对象的根。所以,这个解决方案还不是我需要的,但它非常接近。

Hope this is useful to someone out there, and if you have any suggestions for improvement, I am all-ears; I wrote this very late last night (i.e. early this morning) and there may be things I am completely overlooking.

希望这对那里的人有用,如果你有任何改进的建议,我全心全意;我昨晚很晚才写到这个(即今天凌晨),我可能会完全忽视这些事情。

Thanks.

谢谢。

#3


8  

There is an objectDiff library which allows you to do that. On its demo page you can see a difference between two javascript objects.

有一个objectDiff库,允许您这样做。在其演示页面上,您可以看到两个javascript对象之间的差异。

#4


5  

You can also try rus-diff https://github.com/mirek/node-rus-diff which generates MongoDB compatible (rename/unset/set) diff.

您还可以尝试使用rus-diff https://github.com/mirek/node-rus-diff生成兼容MongoDB(重命名/取消设置/设置)差异。

For your example objects:

对于您的示例对象:

var person1 = {
  FirstName: "John",
  LastName: "Doh",
  Age: 30,
  EMailAddresses: ["john.doe@gmail.com", "jd@initials.com"],
  Children: [
    {
      FirstName: "Sara",
      LastName: "Doe",
      Age: 2
    }, {
      FirstName: "Beth",
      LastName: "Doe",
      Age: 5
    }
  ]
};

var person2 = {
  FirstName: "John",
  LastName: "Doe",
  Age: 33,
  EMailAddresses: ["john.doe@gmail.com", "jdoe@hotmail.com"],
  Children: [
    {
      FirstName: "Sara",
      LastName: "Doe",
      Age: 3
    }, {
      FirstName: "Bethany",
      LastName: "Doe",
      Age: 5
    }
  ]
};

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

console.log(rusDiff(person1, person2))

It generates a list of sets:

它生成一组集:

{ '$set': 
   { 'Age': 33,
     'Children.0.Age': 3,
     'Children.1.FirstName': 'Bethany',
     'EMailAddresses.1': 'jdoe@hotmail.com',
     'LastName': 'Doe' } }

#5


4  

Solution 1

解决方案1

Use JSON.stringify(obj) to get a string representation of the objects you want to compare. Save the string to a file. Use any diff viewer to compare the text files.

使用JSON.stringify(obj)获取要比较的对象的字符串表示形式。将字符串保存到文件。使用任何diff查看器来比较文本文件。

Note: JSON.stringify will ignore properties that point to function definitions.

注意:JSON.stringify将忽略指向函数定义的属性。

Solution 2

解决方案2

This could do what you want with some modification, it is a modified version of the function _.isEqual (http://documentcloud.github.com/underscore/). Please feel free to suggest any modifications! I wrote it to figure out where the first difference between two objects occur.

这可以通过一些修改来做你想要的,它是函数_.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;
};

#6


3  

This script has a NPM version as well, if you are using NodeJS. https://github.com/NV/objectDiff.js

如果您使用NodeJS,此脚本也具有NPM版本。 https://github.com/NV/objectDiff.js

Rejoice.

麾。

#7


1  

I recently wrote a module to do this, because I wasn't satisfied with the numerous diffing modules I found (I listed a bunch of the most popular modules and why they weren't acceptable in the readme of my module). Its called odiff: https://github.com/Tixit/odiff . Here's an example:

我最近写了一个模块来做这个,因为我对我发现的众多差异模块不满意(我列出了一些最流行的模块以及为什么它们在我的模块的自述文件中是不可接受的)。它叫做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}]}
]
*/

#8


0  

None of the libraries I found were sufficient so I wrote my own AngularJS factory. It compares the objects in both ways and returns only the difference within the same structure.

我找到的所有图书馆都不够,所以我写了自己的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;
}]);

#9


0  

var d = { 
   FirstName : "John", 
   LastName : "Doh", 
   Age : 30, 
   EMailAddresses : [
      "john.doe@gmail.com", 
      "jd@initials.com"
   ], 
   Children : [ 
      { 
         FirstName : "Sara", 
         LastName : "Doe", 
         Age : 2 
      }, { 
         FirstName : "Beth", 
         LastName : "Doe", 
         Age : 5 
      } 
   ] 
};

var f = { 
   FirstName : "John", 
   LastName : "Doe", 
   Age : 33, 
   EMailAddresses : [
      "john.doe@gmail.com", 
      "jdoe@hotmail.com"
   ], 
   Children : [ 
      { 
         FirstName : "Sara", 
         LastName : "Doe", 
         Age : 3 
      }, { 
         FirstName : "Bethany", 
         LastName : "Doe", 
         Age : 5 
      } 
   ] 
};

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

		}
	}else{
		flag	= 0;
	}
	if(flag)
		return true
	else
		return false;
}
function comp_arr(a,b){
	flag = 1;
	if(a.length == b.length ){
		for(var i=0,l=a.length;i<l;i++){
			type1    = Object.prototype.toString.call(a[i])
			type2    = Object.prototype.toString.call(b[i])
			if(type1 == type2) {
				if(type1 == '[object String]' || type1 == '[object Number]' ){
					if( a[i] != b[i]){
						flag = 0;
						break;
					}	
				}else if(type1 == '[object Array]'){
					var result = comp_arr(a[i],b[i]);
					if(!result)
						flag = 0;
				}else if(type1  == '[object Object]'){
					var result = comp_obj(a[i],b[i])
					if(!result)
						flag = 0;
				}	
			}else{
				flag = 0;
				break;	
			}
		}
	}else
		 flag = 0;
	if(flag)
		return true
	else
		return false;
}
function create(t,attr,parent_node,innerdata){
	var dom = document.createElement(t)
	for(key in attr){
		dom.setAttribute(key,attr[key])
	}
	dom.innerHTML = innerdata;
	parent_node.appendChild(dom)
	return dom;
}
window.onload = function () {
	for(key in f){
		if(!(key in d)) 
			resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
		type1    = Object.prototype.toString.call(f[key])
		type2    = Object.prototype.toString.call(d[key])
		if(type1 == type2){
			if(type1 == '[object String]' || type1 == '[object Number]' ){
				if(f[key] != d[key])
					resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
			}else if(type1 == '[object Array]'){
				var result = comp_arr(f[key],d[key]);
				if(!result)
					resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
			}else if(type1 == '[object Object]'){
				var result = comp_obj(f[key],d[key])	
				if(!result)
					resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
			}
		}else 
			resultobj.push({'Property_name':key,'f_value':JSON.stringify(f[key]),'d_value':JSON.stringify(d[key])})
	}
	var tb = document.getElementById('diff');
	var s1 = document.getElementById('source1');
	var s2 = document.getElementById('source2');
	s1.innerHTML = 'Object 1 :'+ JSON.stringify(f)
	s2.innerHTML = 'Object 2 :'+JSON.stringify(d)
	resultobj.forEach(function(data,i){
			tr_dom = create('tr',{},tb,'')
			no = create('td',{},tr_dom,i+1)
			Object.keys(data).forEach(function(tr){
				td_dom = create('td',{},tr_dom,data[tr])
			})
	})
}
<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>