Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a diff of two json-objects

Scenario: I want a function that compares two JSON-objects, and returns a JSON-object with a list of the differences and if possible more data such as coverage metrics.

var madrid = '{"type":"team","description":"Good","trophies":[{"ucl":"10"}, {"copa":"5"}]}'; var barca = '{"type":"team","description":"Bad","trophies":[{"ucl":"3"}]}'; 

If i ran compare(madrid, barca) the returned object could look something like:

{"description" : "Bad", "trophies":[{"ucl":"3"}, {"copa":"5"}]};  

Or something similar, you get the idea.

Does anyone know of a solution to this? I've already found one plugin, but I'd like to know if there are any alternatives.

like image 209
Soroush Hakami Avatar asked Dec 08 '11 13:12

Soroush Hakami


1 Answers

It's possible to use a recursive function that iterates by the object keys. Then use the Object.is to test for NaN and null. Then test if the second object is the type that cast to false like 0, NaN, or null. List the keys of both objects and concatenate them to test of missing keys in the obj1 and then iterate it.

When there is a difference between the same key values, it stores the value of object2 and proceeds. If both key values are object means that can be recursively compared and so it does.

function diff(obj1, obj2) {      const result = {};      if (Object.is(obj1, obj2)) {          return undefined;      }      if (!obj2 || typeof obj2 !== 'object') {          return obj2;      }      Object.keys(obj1 || {}).concat(Object.keys(obj2 || {})).forEach(key => {          if(obj2[key] !== obj1[key] && !Object.is(obj1[key], obj2[key])) {              result[key] = obj2[key];          }          if(typeof obj2[key] === 'object' && typeof obj1[key] === 'object') {              const value = diff(obj1[key], obj2[key]);              if (value !== undefined) {                  result[key] = value;              }          }      });      return result;  }

The code above is BSD licensed and can be used anywhere.

Test link: https://jsfiddle.net/gartz/vy9zaof2/54/

An important observation, this will convert arrays to objects and compare the values in the same index position. There are many other ways to compare arrays not covered by this function due to the required extra complexity.

EDIT 2/15/2019: This answer was changed to add the new ES2017 syntax and fix use-cases from comments.


This is just a kickoff, I haven't tested it, but I began with a filter or comparator function, that is recursive, change it however you need to get priority results.

function filter(obj1, obj2) {     var result = {};     for(key in obj1) {         if(obj2[key] != obj1[key]) result[key] = obj2[key];         if(typeof obj2[key] == 'array' && typeof obj1[key] == 'array')              result[key] = arguments.callee(obj1[key], obj2[key]);         if(typeof obj2[key] == 'object' && typeof obj1[key] == 'object')              result[key] = arguments.callee(obj1[key], obj2[key]);     }     return result; } 

Tests: http://jsfiddle.net/gartz/Q3BtG/2/

like image 192
Gabriel Gartz Avatar answered Sep 25 '22 15:09

Gabriel Gartz