Reputation: 36937
Is it possible to compare 2 sets of json objects for a difference? What I have is a script thats polling for JSON object via jquery $post(). What I want to do is take the object that was just polled and compare it to a stored one. Where if there is any changes from one to the other apply them to the stored object or replace it (either way) but from the UI perspective I am working with seamlessly apply the changes to what the JSON object is for by finding the differences between the 2. I want to do this because right now I have it so the UI is completely reloading per poll regardless of change or not which basically looks like ** from a UX perspective.
I figure if I can find the differences if any between the 2 objects I would fire off a function that I would have edit the UI specific to the differences.
Upvotes: 9
Views: 23012
Reputation: 590
Here is my code:
function getDifferences(oldObj, newObj) {
var diff = {};
for (var k in oldObj) {
if (!(k in newObj)){
diff[k] = undefined; // old key does not exist in new
} else {
switch (typeof oldObj[k]){
case "array": {
String(oldObj[k]) !== String(newObj[k]) ? diff[k] = newObj[k] : null;
break;
} case "object": {
Object.keys(oldObj[k]).forEach(key =>{
if(oldObj[k][key] !== newObj[k][key]){
if(diff[k]){
diff[k][key] = newObj[k][key];
} else {
diff[k] = {[key]: newObj[k][key]}
}
}
});
//JSON.stringify(oldObj[k]) !== JSON.stringify(newObj[k]) ? diff[k] = newObj[k] : null; Optional basic comparision
break;
} default: { //Values are strings or numbers
oldObj[k] !== newObj[k] ? diff[k] = newObj[k] : null;
break;
}
}
}
}
for (k in newObj) {
if (!(k in oldObj))
diff[k] = newObj[k]; // property is new
}
return diff;
}
We get the type of the elements and make an internal comparision of equality.
Upvotes: 0
Reputation: 16282
Sorry to answer old thread but my answer may help others who may face the same issue.the most easiest & shortest code to compare two json object as follows. thanks
<script type="text/javascript">
$(document).ready(function () {
var a = { "id": "210", "memberlist": "john" };
var b = { "id": "210", "memberlist": "john1" };
alert(JSON.stringify(a) != JSON.stringify(b) ? 'not same' : ' same');
});
</script>
Upvotes: 6
Reputation: 25265
var objectsAreEqual = function(obj1, x){
var MAX_DEPTH = 10;
var testEq = function(obj1, x, depth){
if(depth < MAX_DEPTH){
for (var p in obj1) {
if(typeof(obj1[p]) !== typeof(x[p])) return false;
if((obj1[p]===null) !== (x[p]===null)) return false;
switch (typeof(obj1[p])) {
case 'undefined':
if (typeof(x[p]) != 'undefined') return false;
break;
case 'object':
if(obj1[p]!==null && x[p]!==null && (obj1[p].constructor.toString() !== x[p].constructor.toString() || !testEq(obj1[p], x[p], depth + 1))) return false;
break;
case 'function':
if (p != 'equals' && obj1[p].toString() != x[p].toString()) return false;
break;
default:
if (obj1[p] !== x[p]) return false;
}
}
}
return true;
};
// this is a little ugly, but the algorithm above fails the following: testEq([[1,2],[]], [[1,2],[1,3]], 0)
return testEq(obj1, x, 0) && testEq(x, obj1, 0);
};
Upvotes: 0
Reputation: 150040
What I want to do is take the object that was just polled and compare it to a stored one. Where if there is any changes from one to the other apply them to the stored object or replace it (either way)
If you would be happy with a really simple "Has it changed in any way? Yes/No" solution, where if it has changed you just replace the previous object with the new one (as per the part of your question that I quoted), then you could save the JSON response before you parse it, i.e., save it in the string format in which your web-server sends it. Then when the next response comes in compare the new string with the old string. If they are different (or if it is the first request) parse the JSON and process it for display as appropriate. Naturally this assumes that your server-side code is creating the JSON strings in a consistent format (and not, e.g., changing the order of the properties).
If we assume you've already got (parsed) objects, an isEqual(a,b)
function really should cope with nested objects, properties that are arrays, etc. This can be done recursively, and simply return true or false, but a getDifferences(a,b)
function is going to get confusing in how it reports the differences within nested objects. Consider this simple example:
old: {"mum" : "Maria", "dad" : "Pierre", "kids" : ["Joe", "Mike", "Louisa"] }
new: {"mum" : "Julie", "dad" : "Pierre", "kids" : ["Joe", "Mary"] }
Is the difference {"mum" : "Julie", "kids" : ["Mary"]}
? The "mum" has changed, and the list of "kids" has changed, but has "Mike" changed to "Mary", or are both "Mike" and "Louisa" gone with "Mary" being new, or...? Maybe it should be "kids": ["Joe","Mary"]
because that's the new value. How do you indicate the deletions? That's just the first example off the top of my head where I don't know how you would want to handle the differences. It could quickly get worse: what if the "kids" array contained objects instead of strings to represent a whole family tree? What if the new "mum" property was ["Maria", "Julie"]
(to allow for step-parents and so forth)?
If for your particular data you know you've only got one-dimensional objects then you can do something simple like the following:
function getDifferences(oldObj, newObj) {
var diff = {};
for (var k in oldObj) {
if (!(k in newObj))
diff[k] = undefined; // property gone so explicitly set it undefined
else if (oldObj[k] !== newObj[k])
diff[k] = newObj[k]; // property in both but has changed
}
for (k in newObj) {
if (!(k in oldObj))
diff[k] = newObj[k]; // property is new
}
return diff;
}
The simplest change to the above to allow for nested objects is to just assume that if a property is an object/array then you only care whether it is different in any way and not dig down to report exactly which "sub-properties" have changed. If so, simply take the above function and change:
else if (oldObj[k] !== newObj[k])
to
else if (!isEqual(oldObj[k],newObj[k]))
Where isEqual()
is one of the many comparison functions floating around the web or on StackOverflow.
(Note: I haven't bothered with .hasOwnProperty()
above because I assume that objects that were returned to an Ajax request as JSON will not be inheriting properties from a prototype chain. Similarly an isEqual()
function for this purpose wouldn't need to worry about properties being functions, it only needs to worry about what is valid in a JSON string.)
Upvotes: 11
Reputation: 1898
One potential solution could be to use jQuery's extend. As long as the objects have the same properties, that would work out nicely.
var newJSON = $.extend({},oldJSON,serverData);
Then you would have preserved the old object, and created a new object with any properties from the old object if they aren't present in the new object and overwritten any existing properties with the properties from the new data from the server.
Upvotes: 0