9

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.

chris
  • 36,115
  • 52
  • 143
  • 252
  • 5
    Your doing it wrong, you don't send similar objects from the server and find the difference manually on the client side. You get the server to _only_ send you the difference. You save bandwidth and computation time. – Raynos Nov 13 '11 at 00:36
  • Which would be awesome if I had control over the server side specturm of the piece I am working on. Unfortunately I am stuck pulling in this data from various sources on the network with what canned functions they gave me to work with. My hands are tied or I would have approached this differently and likely in a fashion you suggested. – chris Nov 13 '11 at 01:30
  • 1
    In this same you probably want something like [json diff](http://tlrobinson.net/projects/javascript-fun/jsondiff/) – Raynos Nov 13 '11 at 01:44
  • 1
    @Raynos That JSON Diff is essentially what I am looking to achieve. Now I just need to figure out how to achieve the same logic or algorithm they used to apply it to my needs. – chris Nov 13 '11 at 02:11

5 Answers5

11

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.)

Community
  • 1
  • 1
nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • Im liking this explination. I am currently setting up a new testing enviroment. So when thats done I will give this a crack and see what I can do with it for my needs, I really appreciate the level of depth you went into to help me understand it as well. Ill come back later with my results if it worked out for me or not. – chris Nov 13 '11 at 02:37
  • isEqual is a lodash native function, What if you just transform your object into String and compare? ´´´else if (String(oldObj[k]) !== String(newObj[k]))´´´ It should work – Jonathan Arias Jul 02 '19 at 15:09
6

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>
Mou
  • 15,673
  • 43
  • 156
  • 275
  • 1
    Yea, but the whole point of the question if I recall was to detect change, then determine those differences. I don't honestly remember what was something like 4 years ago, anywho, to comment on this though, this will just tell you if they are literally the same or not.. Which may work for that level of the idea ie: are these the same true or false, but even then, on large objects, this may also cause some strain on the client, more so than looping over it and comparing. Cause with larger objects this could possibly take up a lot of client side memory – chris Feb 26 '15 at 20:22
0

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.

Jonathan Arias
  • 471
  • 7
  • 18
0

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.

bstakes
  • 1,898
  • 14
  • 12
  • from the OP "Where if there is any changes from one to the other apply them to the stored object or replace it (either way)" – bstakes Nov 13 '11 at 01:45
  • You misinterpreted him. He wants to know what changed, not just get a new object. If he wanted the latter then he could have done `var newJSON = serverData` – Raynos Nov 13 '11 at 01:47
  • @Raynos - No, as bstakes quoted from the question, the OP said to determine if there are differences, then "apply them to the stored object **or replace it (either way)**". I take that to mean that the ideal solution will report the differences, but if that can't be done (or can't be done easily) then a straight replace is acceptable. – nnnnnn Nov 13 '11 at 02:16
  • @nnnnnn - so why would you merge them. you could just use the new one instead. This answer doesn't answer the question at all. it's shows deep problems of understanding the problem at hand. – vsync Sep 23 '14 at 16:27
0
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); 

};
morgancodes
  • 25,055
  • 38
  • 135
  • 187