Reputation: 5930
Consider the following function's blueprint which tries to compare two objects:
function objectCompare(a,b,path){
for (var prop in a) {
path=prop;
if (a.hasOwnProperty(prop) && !(b.hasOwnProperty(prop))) {
...
return false;
}
...
if (detectType(a[prop])==='Object'){
if (!objectCompare(a[prop],b[prop],path))
return false;
}
...
}
return true;
}
detectType
is my own function which checks the type of a variable. My problem is that I want to enrich variable path
every time we have a recursive call. However, at the same time when recursive calls finish, path
has to traverse the remaining property names of the initial object without being enriched...
Imagine the following objects:
var Obj1 = {
p1: 's',
p2: {
p1: {a: { propA: 'a', propB: 'b' }},
p2: 'g',
}
};
var Obj2 = {
p1: 's',
p2: {
p1: {a: { propA: 'a', propB: 'c' }},
p2: 'g',
}
};
I want path
when function objectCompare
returns to have the following value: p2.p1.a.propB
Namely, the point which makes the two objects different. How may I achieve that?
Upvotes: 0
Views: 235
Reputation: 214959
You have to add the current key to the path and pass the new path to the recursive call. Consider:
console.info=function(x){document.write('<pre>'+JSON.stringify(x,0,3)+'</pre>')}
//--
// compare: return path if a,b are different, null otherwise
function compare(a, b, path) {
var ta = typeof a,
tb = typeof b;
// different types - fail
if (ta !== tb) {
return path;
}
// scalars - null if equal, path if not
if (ta !== 'object') {
return a === b ? null : path;
}
// make a set of keys from both objects
var keys = Object.keys(a).concat(Object.keys(b)).filter(function(x, i, self) {
return self.indexOf(x) === i;
});
// for each key in set, compare values
var res = null;
keys.some(function(k) {
return res = compare(a[k], b[k], path.concat(k));
});
// return null or the first failed path
return res;
}
//
var Obj1 = {
p1: 's',
p2: {
p1: {a: { propA: 'a', propB: 'b' }},
p2: 'g',
}
};
var Obj2 = {
p1: 's',
p2: {
p1: {a: { propA: 'a', propB: 'c' }},
p2: 'g',
}
};
var res = compare(Obj1, Obj2, [])
console.info(res);
Upvotes: 1
Reputation: 350242
The library deep-diff
does what you need.
The above reference presents this example code:
var diff = require('deep-diff').diff;
var lhs = {
name: 'my object',
description: 'it\'s an object!',
details: {
it: 'has',
an: 'array',
with: ['a', 'few', 'elements']
}
};
var rhs = {
name: 'updated object',
description: 'it\'s an object!',
details: {
it: 'has',
an: 'array',
with: ['a', 'few', 'more', 'elements', { than: 'before' }]
}
};
var differences = diff(lhs, rhs);
The code snippet above would result in the following structure describing the differences:
[ { kind: 'E',
path: [ 'name' ],
lhs: 'my object',
rhs: 'updated object' },
{ kind: 'E',
path: [ 'details', 'with', 2 ],
lhs: 'elements',
rhs: 'more' },
{ kind: 'A',
path: [ 'details', 'with' ],
index: 3,
item: { kind: 'N', rhs: 'elements' } },
{ kind: 'A',
path: [ 'details', 'with' ],
index: 4,
item: { kind: 'N', rhs: { than: 'before' } } } ]
Notably the path
property is much like your desired output.
Upvotes: 0