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.
You can use rus-diff https://github.com/mirek/node-rus-diff which creates MongoDB compatible (rename/unset/set) diff:
// npm install rus-diff
var madrid = {"type":"team","description":"Good","trophies":[{"ucl":"10"}, {"copa":"5"}]};
var barca = {"type":"team","description":"Bad","trophies":[{"ucl":"3"}]};
var rusDiff = require('rus-diff').rusDiff
console.log(rusDiff(madrid, barca))
Outputs:
{ '$unset': { 'trophies.1': true },
'$set': { description: 'Bad', 'trophies.0.ucl': '3' } }
contributing back my changes to Gabriel Gartz version. This one works in strict mode and removes the array check - will always be false. It also removes empty nodes from the diff.
//http://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object
var isEmptyObject = function(obj) {
var name;
for (name in obj) {
return false;
}
return true;
};
//http://stackoverflow.com/questions/8431651/getting-a-diff-of-two-json-objects
var diff = function(obj1, obj2) {
var result = {};
var change;
for (var key in obj1) {
if (typeof obj2[key] == 'object' && typeof obj1[key] == 'object') {
change = diff(obj1[key], obj2[key]);
if (isEmptyObject(change) === false) {
result[key] = change;
}
}
else if (obj2[key] != obj1[key]) {
result[key] = obj2[key];
}
}
return result;
};
We can go with JSON.Stringify
var obj1 = {
"Agent": "12819",
"Beneficiary": "476949",
"BillingDetail": "13772",
"BillingInvoice": "914548",
"Claim": "1192",
"Customer": "656320",
"LifeAssured": "228493",
"Payment": "1091661",
"Policy/Proposal": "263196",
"Product": "9",
"ProductComponent": "53",
"ProductComponentOption": "2239791",
"ProductOption": "568785",
"TransactionDetail": "4289240"
}
var obj2 = {
"Agent": "1289",
"Beneficiary": "47694",
"BillingDetail": "13772",
"BillingInvoice": "914548",
"Claim": "1192",
"Customer": "656320",
"LifeAssured": "22893",
"Payment": "1091661",
"Policy/Proposal": "26316",
"Product": "2",
"ProductComponent": "52",
"ProductComponentOption": "223971",
"ProductOption": "56885",
"TransactionDetail": "4289240"
}
JSON.stringify(obj1) == JSON.stringify(obj2)
false
var obj3 = {
"Agent": "1289",
"Beneficiary": "47694",
"BillingDetail": "13772",
"BillingInvoice": "914548",
"Claim": "1192",
"Customer": "656320",
"LifeAssured": "22893",
"Payment": "1091661",
"Policy/Proposal": "26316",
"Product": "2",
"ProductComponent": "52",
"ProductComponentOption": "223971",
"ProductOption": "56885",
"TransactionDetail": "4289240"
}
JSON.stringify(obj2) == JSON.stringify(obj3)
true
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/
Suppose we want to compare two same objects and subtract if object field count match then follow the bellow code.
var obj1 = {
"Agent": "12819",
"Beneficiary": "476949",
"BillingDetail": "13772",
"BillingInvoice": "914548",
"Claim": "1192",
"Customer": "656320",
"LifeAssured": "228493",
"Payment": "1091661",
"Policy/Proposal": "263196",
"Product": "9",
"ProductComponent": "53",
"ProductComponentOption": "2239791",
"ProductOption": "568785",
"TransactionDetail": "4289240"
}
var obj2 = {
"Agent": "1289",
"Beneficiary": "47694",
"BillingDetail": "13772",
"BillingInvoice": "914548",
"Claim": "1192",
"Customer": "656320",
"LifeAssured": "22893",
"Payment": "1091661",
"Policy/Proposal": "26316",
"Product": "2",
"ProductComponent": "52",
"ProductComponentOption": "223971",
"ProductOption": "56885",
"TransactionDetail": "4289240"
}
function diff(obj1, obj2) {
const result = {};
for(var o1 in obj1){
if(obj2[o1]){
result[o1] = obj1[o1] - obj2[o1];
}
}
return result;
}
console.log(diff(obj1,obj2))
Result:
{
"Agent": 11530,
"Beneficiary": 429255,
"BillingDetail": 0,
"BillingInvoice": 0,
"Claim": 0,
"Customer": 0,
"LifeAssured": 205600,
"Payment": 0,
"Policy/Proposal": 236880,
"Product": 7,
"ProductComponent": 1,
"ProductComponentOption": 2015820,
"ProductOption": 511900,
"TransactionDetail": 0
}