4

This is a question that involves a more complicated way of comparison, Thus it's not a duplicate

I've created a JqTree, that when the user changes its tree structure, both "old" JSON and "new" JSON structures should be compared, and it should be shown only the values of the JSON that has been changed.

For example:

[{"name":"node1","id":1,"is_open":true,"children":
    [
      {"name":"child1","id":2},
      {"name":"child2","id":3}
    ]
}]

Example

After the client has placed child1 under child2

[{"name":"node1","id":1,"is_open":true,"children":
  [
    {"name":"child2","id":3},
    {"name":"child1","id":2}
  ]
}]

example

I just would like to compare them and check which values were changed and show them with an alert, which, in this case, would be:

{"name":"child2","id":3},
{"name":"child1","id":2}

So far I have this tiny code that compares them:

JSON.stringify(object1) === JSON.stringify(object2); //I know it's not too reliable.

But I'm looking for something that checks the "difference" and extracts it from the JSON.

Thanks in advance.

Community
  • 1
  • 1
Kyle
  • 1,568
  • 2
  • 20
  • 46
  • 1
    Computing the difference between two data structures is not a simple affair. [Here's a related stackexchange question.](http://cstheory.stackexchange.com/questions/10205/efficient-diff-algorithm-for-trees-and-levenshtein-distance) – Pointy Jul 08 '15 at 14:24
  • @Pointy indeed, but isn't easier to transform them into a different "type", and then compare? if I transform them, for example, into string like stringifying them. I'm sorry if it sounds dumb – Kyle Jul 08 '15 at 14:27
  • Well you can do that, but then you get a string difference; a character-by-character difference, in other words. Now you're faced with the problem of interpreting that result in terms of the data structure, which (as I understand it) is the actual model involved. – Pointy Jul 08 '15 at 14:35
  • @Pointy yeah I see it. What do you suggest? given the fact that my tree won't be more complex that on my example. – Kyle Jul 08 '15 at 14:39
  • I'm not sure how I would approach it. Isn't this sort-of the core of what the React framework does? – Pointy Jul 08 '15 at 14:41
  • React framework? I think now it's out of my knowledge, I will study about it right now, thanks @Pointy for informing me :). One more question, do you have an example that approaches to it, anything? – Kyle Jul 08 '15 at 14:43
  • 1
    You could write a recursive function to walk down the old tree node by node, compare to the new tree, when a difference is found add that node to a collection and at the end alert the collection. – lwalden Jul 08 '15 at 14:49
  • @lwalden interesting, could you explain it a little bit more clearly, I have some difficulty on understanding it. AKA Can you draw it as a flowchart or something, you can write/draw it as an answer and I'd be glad to mark it as the answer :) – Kyle Jul 08 '15 at 14:53
  • @Calne, musicin3d's answer below is essentially what I was describing. – lwalden Jul 08 '15 at 18:14

1 Answers1

8

Here you go: http://jsfiddle.net/musicin3d/cf5ddod1/3/

Edited down version for your no-clicking pleasure:

// Call this function.
// The others are helpers for this one.
function getDiff(a, b){
    var diff = (isArray(a) ? [] : {});
    recursiveDiff(a, b, diff);
    return diff;
}

function recursiveDiff(a, b, node){
    var checked = [];

    for(var prop in a){
        if(typeof b[prop] == 'undefined'){
            addNode(prop, '[[removed]]', node);
        }
        else if(JSON.stringify(a[prop]) != JSON.stringify(b[prop])){
            // if value
            if(typeof b[prop] != 'object' || b[prop] == null){
                addNode(prop, b[prop], node);
            }
            else {
                // if array
                if(isArray(b[prop])){
                   addNode(prop, [], node);
                   recursiveDiff(a[prop], b[prop], node[prop]);
                }
                // if object
                else {
                    addNode(prop, {}, node);
                    recursiveDiff(a[prop], b[prop], node[prop]);
                }
            }
        }
    }
}

function addNode(prop, value, parent){
        parent[prop] = value;
}

function isArray(obj){
    return (Object.prototype.toString.call(obj) === '[object Array]');
}

See the link above for more details. There is a comment that explains some of my assumptions.

This is an example of how to use recursion to solve your problem. If you're not familiar with recursion, I suggest you do some reading. Here's a SO article about it: What is recursion and when should I use it?

Of note:
Using JSON.stringify like I did is not a good idea. It's convenient for me as the programmer because my program can "look ahead" to see if there's a change down each path, but that comes at a cost. I'm already traversing the tree structure to do my work, and JSON.stringify also traverses the tree structure of every object I send every time I call it. In computer science we call this a worst case scenario of O(n!), less formally referred to as "very slow." A better design would just traverse the entire tree and keep track of how it got to where it was. When it came to a dead end (called a "leaf" node"), it would use this knowledge to add the necessary data structure to the diff variable all at once. This would mean our program would have to traverse the entire data structure, but our code would be the only thing doing it. So each node would be processed only once.

Still, this should give you an idea of what others were suggesting.

musicin3d
  • 1,028
  • 1
  • 12
  • 22
  • 1
    Also worth pointing out: If the order of an array changes, that will show up as a difference. – musicin3d Jul 08 '15 at 17:49
  • Amazing work, it's art, thanks for giving this marvelous example, I'm going to break it down to see how it works – Kyle Jul 09 '15 at 11:03