How to do a deep comparison between 2 objects with lodash?

JavascriptLodash

Javascript Problem Overview


I have 2 nested objects which are different and I need to know if they have a difference in one of their nested properties.

var a = {};
var b = {};

a.prop1 = 2;
a.prop2 = { prop3: 2 };

b.prop1 = 2;
b.prop2 = { prop3: 3 };

The object could be much more complex with more nested properties. But this one is a good example. I have the option to use recursive functions or something with lodash...

Javascript Solutions


Solution 1 - Javascript

An easy and elegant solution is to use _.isEqual, which performs a deep comparison:

var a = {};
var b = {};

a.prop1 = 2;
a.prop2 = { prop3: 2 };

b.prop1 = 2;
b.prop2 = { prop3: 3 };

console.log(_.isEqual(a, b)); // returns false if different

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

However, this solution doesn't show which property is different.

Solution 2 - Javascript

If you need to know which properties are different, use reduce():

_.reduce(a, function(result, value, key) {
    return _.isEqual(value, b[key]) ?
        result : result.concat(key);
}, []);
// → [ "prop2" ]

Solution 3 - Javascript

For anyone stumbling upon this thread, here's a more complete solution. It will compare two objects and give you the key of all properties that are either only in object1, only in object2, or are both in object1 and object2 but have different values:

/*
 * Compare two objects by reducing an array of keys in obj1, having the
 * keys in obj2 as the intial value of the result. Key points:
 *
 * - All keys of obj2 are initially in the result.
 *
 * - If the loop finds a key (from obj1, remember) not in obj2, it adds
 *   it to the result.
 *
 * - If the loop finds a key that are both in obj1 and obj2, it compares
 *   the value. If it's the same value, the key is removed from the result.
 */
function getObjectDiff(obj1, obj2) {
    const diff = Object.keys(obj1).reduce((result, key) => {
        if (!obj2.hasOwnProperty(key)) {
            result.push(key);
        } else if (_.isEqual(obj1[key], obj2[key])) {
            const resultKeyIndex = result.indexOf(key);
            result.splice(resultKeyIndex, 1);
        }
        return result;
    }, Object.keys(obj2));

    return diff;
}

Here's an example output:

// Test
let obj1 = {
    a: 1,
    b: 2,
    c: { foo: 1, bar: 2},
    d: { baz: 1, bat: 2 }
}

let obj2 = {
    b: 2, 
    c: { foo: 1, bar: 'monkey'}, 
    d: { baz: 1, bat: 2 }
    e: 1
}
getObjectDiff(obj1, obj2)
// ["c", "e", "a"]

If you don't care about nested objects and want to skip lodash, you can substitute the _.isEqual for a normal value comparison, e.g. obj1[key] === obj2[key].

Solution 4 - Javascript

Based on the answer by Adam Boduch, I wrote this function which compares two objects in the deepest possible sense, returning paths that have different values as well as paths missing from one or the other object.

The code was not written with efficiency in mind, and improvements in that regard are most welcome, but here is the basic form:

var compare = function (a, b) {

  var result = {
    different: [],
    missing_from_first: [],
    missing_from_second: []
  };

  _.reduce(a, function (result, value, key) {
    if (b.hasOwnProperty(key)) {
      if (_.isEqual(value, b[key])) {
        return result;
      } else {
        if (typeof (a[key]) != typeof ({}) || typeof (b[key]) != typeof ({})) {
          //dead end.
          result.different.push(key);
          return result;
        } else {
          var deeper = compare(a[key], b[key]);
          result.different = result.different.concat(_.map(deeper.different, (sub_path) => {
            return key + "." + sub_path;
          }));

          result.missing_from_second = result.missing_from_second.concat(_.map(deeper.missing_from_second, (sub_path) => {
            return key + "." + sub_path;
          }));

          result.missing_from_first = result.missing_from_first.concat(_.map(deeper.missing_from_first, (sub_path) => {
            return key + "." + sub_path;
          }));
          return result;
        }
      }
    } else {
      result.missing_from_second.push(key);
      return result;
    }
  }, result);

  _.reduce(b, function (result, value, key) {
    if (a.hasOwnProperty(key)) {
      return result;
    } else {
      result.missing_from_first.push(key);
      return result;
    }
  }, result);

  return result;
}

You can try the code using this snippet (running in full page mode is recommended):

var compare = function (a, b) {

  var result = {
    different: [],
    missing_from_first: [],
    missing_from_second: []
  };

  _.reduce(a, function (result, value, key) {
    if (b.hasOwnProperty(key)) {
      if (_.isEqual(value, b[key])) {
        return result;
      } else {
        if (typeof (a[key]) != typeof ({}) || typeof (b[key]) != typeof ({})) {
          //dead end.
          result.different.push(key);
          return result;
        } else {
          var deeper = compare(a[key], b[key]);
          result.different = result.different.concat(_.map(deeper.different, (sub_path) => {
            return key + "." + sub_path;
          }));

          result.missing_from_second = result.missing_from_second.concat(_.map(deeper.missing_from_second, (sub_path) => {
            return key + "." + sub_path;
          }));

          result.missing_from_first = result.missing_from_first.concat(_.map(deeper.missing_from_first, (sub_path) => {
            return key + "." + sub_path;
          }));
          return result;
        }
      }
    } else {
      result.missing_from_second.push(key);
      return result;
    }
  }, result);

  _.reduce(b, function (result, value, key) {
    if (a.hasOwnProperty(key)) {
      return result;
    } else {
      result.missing_from_first.push(key);
      return result;
    }
  }, result);

  return result;
}

var a_editor = new JSONEditor($('#a')[0], {
  name: 'a',
  mode: 'code'
});
var b_editor = new JSONEditor($('#b')[0], {
  name: 'b',
  mode: 'code'
});

var a = {
  same: 1,
  different: 2,
  missing_from_b: 3,
  missing_nested_from_b: {
    x: 1,
    y: 2
  },
  nested: {
    same: 1,
    different: 2,
    missing_from_b: 3
  }
}

var b = {
  same: 1,
  different: 99,
  missing_from_a: 3,
  missing_nested_from_a: {
    x: 1,
    y: 2
  },
  nested: {
    same: 1,
    different: 99,
    missing_from_a: 3
  }
}

a_editor.set(a);
b_editor.set(b);

var result_editor = new JSONEditor($('#result')[0], {
  name: 'result',
  mode: 'view'
});

var do_compare = function() {
  var a = a_editor.get();
  var b = b_editor.get();
  result_editor.set(compare(a, b));
}

#objects {} #objects section {
  margin-bottom: 10px;
}
#objects section h1 {
  background: #444;
  color: white;
  font-family: monospace;
  display: inline-block;
  margin: 0;
  padding: 5px;
}
.jsoneditor-outer, .ace_editor {
min-height: 230px !important;
}
button:hover {
  background: orangered;
}
button {
  cursor: pointer;
  background: red;
  color: white;
  text-align: left;
  font-weight: bold;
  border: 5px solid crimson;
  outline: 0;
  padding: 10px;
  margin: 10px 0px;
}

<link href="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.5.10/jsoneditor.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.5.10/jsoneditor.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="objects">
  <section>
    <h1>a (first object)</h1>
    <div id="a"></div>
  </section>
  <section>
    <h1>b (second object)</h1>
    <div id="b"></div>
  </section>
  <button onClick="do_compare()">compare</button>
  <section>
    <h1>result</h1>
    <div id="result"></div>
  </section>
</div>

Solution 5 - Javascript

Here's a concise solution using Lodash:

_.differenceWith(a, b, _.isEqual);

Note both inputs need to be arrays (possibly an array of one object).

Solution 6 - Javascript

To recursively show how an object is different with other you can use _.reduce combined with _.isEqual and _.isPlainObject. In this case you can compare how a is different with b or how b is different with a:

const objectA = { a: { 1: "SAME WILL BE MISSING IN RESULT", 2: "BBB", 3: [1, 2, 3] }, b: "not", c: "foo bar" }; const objectB = { a: { 1: "SAME WILL BE MISSING IN RESULT", 2: [1, 2] }, b: "foo", c: "bar" };

const diff = function(obj1, obj2) { return .reduce(obj1, function(result, value, key) { if (.isPlainObject(value)) { result[key] = diff(value, obj2[key]); } else if (!_.isEqual(value, obj2[key])) { result[key] = value; } return result; }, {}); };

const diffAOverB = diff(objectA, objectB); const diffBOverA = diff(objectA, objectB); console.log(diffAOverB); console.log(diffBOverA);

<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

Solution 7 - Javascript

This code returns an object with all properties that have a different value and also values of both objects. Useful to logging the difference.

var allkeys = _.union(_.keys(obj1), _.keys(obj2));
var difference = _.reduce(allkeys, function (result, key) {
  if ( !_.isEqual(obj1[key], obj2[key]) ) {
    result[key] = {obj1: obj1[key], obj2: obj2[key]}
  }
  return result;
}, {});

Solution 8 - Javascript

Simple use _.isEqual method, it will work for all comparing...

> * Note: This method supports comparing arrays, array buffers, > booleans, > * date objects, error objects, maps, numbers, Object objects, regexes, > * sets, strings, symbols, and typed arrays. Object objects are compared > * by their own, not inherited, enumerable properties. Functions and DOM > * nodes are not supported.

So if you have below:

 const firstName = {name: "Alireza"};
 const otherName = {name: "Alireza"};

If you do: _.isEqual(firstName, otherName);,

it will return true

And if const fullName = {firstName: "Alireza", familyName: "Dezfoolian"};

If you do: _.isEqual(firstName, fullName);,

will return false

Solution 9 - Javascript

As it was asked, here's a recursive object comparison function. And a bit more. Assuming that primary use of such function is object inspection, I have something to say. Complete deep comparison is a bad idea when some differences are irrelevant. For example, blind deep comparison in TDD assertions makes tests unnecessary brittle. For that reason, I'd like to introduce a much more valuable partial diff. It is a recursive analogue of a previous contribution to this thread. It ignores keys not present in a

var bdiff = (a, b) =>
    _.reduce(a, (res, val, key) =>
        res.concat((_.isPlainObject(val) || _.isArray(val)) && b
            ? bdiff(val, b[key]).map(x => key + '.' + x) 
            : (!b || val != b[key] ? [key] : [])),
        []);

BDiff allows checking for expected values while tolerating other properties, which is exactly what you want for automatic inspection. This allows building all kinds of advanced assertions. For example:

var diff = bdiff(expected, actual);
// all expected properties match
console.assert(diff.length == 0, "Objects differ", diff, expected, actual);
// controlled inequality
console.assert(diff.length < 3, "Too many differences", diff, expected, actual);

Returning to the complete solution. Building a full traditional diff with bdiff is trivial:

function diff(a, b) {
    var u = bdiff(a, b), v = bdiff(b, a);
    return u.filter(x=>!v.includes(x)).map(x=>' < ' + x)
    .concat(u.filter(x=>v.includes(x)).map(x=>' | ' + x))
    .concat(v.filter(x=>!u.includes(x)).map(x=>' > ' + x));
};

Running above function on two complex objects will output something similar to this:

 [  " < components.0.components.1.components.1.isNew",  " < components.0.cryptoKey",  " | components.0.components.2.components.2.components.2.FFT.min",  " | components.0.components.2.components.2.components.2.FFT.max",  " > components.0.components.1.components.1.merkleTree",  " > components.0.components.2.components.2.components.2.merkleTree",  " > components.0.components.3.FFTResult" ]

Finally, in order to have a glimpse into how the values differ, we may want to directly eval() the diff output. For that, we need an uglier version of bdiff that outputs syntactically correct paths:

// provides syntactically correct output
var bdiff = (a, b) =>
    _.reduce(a, (res, val, key) =>
        res.concat((_.isPlainObject(val) || _.isArray(val)) && b
            ? bdiff(val, b[key]).map(x => 
                key + (key.trim ? '':']') + (x.search(/^\d/)? '.':'[') + x)
            : (!b || val != b[key] ? [key + (key.trim ? '':']')] : [])),
        []);

// now we can eval output of the diff fuction that we left unchanged
diff(a, b).filter(x=>x[1] == '|').map(x=>[x].concat([a, b].map(y=>((z) =>eval('z.' + x.substr(3))).call(this, y)))));

That will output something similar to this:

[" | components[0].components[2].components[2].components[2].FFT.min", 0, 3]
[" | components[0].components[2].components[2].components[2].FFT.max", 100, 50]

MIT license ;)

Solution 10 - Javascript

Here is a simple Typescript with Lodash deep difference checker which will produce a new object with just the differences between an old object and a new object.

For example, if we had:

const oldData = {a: 1, b: 2};
const newData = {a: 1, b: 3};

the resulting object would be:

const result: {b: 3};

It is also compatible with multi-level deep objects, for arrays it may need some tweaking.

import * as _ from "lodash";

export const objectDeepDiff = (data: object | any, oldData: object | any) => {
  const record: any = {};
  Object.keys(data).forEach((key: string) => {
    // Checks that isn't an object and isn't equal
    if (!(typeof data[key] === "object" && _.isEqual(data[key], oldData[key]))) {
      record[key] = data[key];
    }
    // If is an object, and the object isn't equal
    if ((typeof data[key] === "object" && !_.isEqual(data[key], oldData[key]))) {
      record[key] = objectDeepDiff(data[key], oldData[key]);
    }
  });
  return record;
};

Solution 11 - Javascript

I took a stab a Adam Boduch's code to output a deep diff - this is entirely untested but the pieces are there:

function diff (obj1, obj2, path) {
    obj1 = obj1 || {};
    obj2 = obj2 || {};

    return _.reduce(obj1, function(result, value, key) {
        var p = path ? path + '.' + key : key;
        if (_.isObject(value)) {
            var d = diff(value, obj2[key], p);
            return d.length ? result.concat(d) : result;
        }
        return _.isEqual(value, obj2[key]) ? result : result.concat(p);
    }, []);
}

diff({ foo: 'lol', bar: { baz: true }}, {}) // returns ["foo", "bar.baz"]

Solution 12 - Javascript

Deep compare using a template of (nested) properties to check

function objetcsDeepEqualByTemplate(objectA, objectB, comparisonTemplate) {
  if (!objectA || !objectB) return false

  let areDifferent = false
  Object.keys(comparisonTemplate).some((key) => {
    if (typeof comparisonTemplate[key] === 'object') {
      areDifferent = !objetcsDeepEqualByTemplate(objectA[key], objectB[key], comparisonTemplate[key])
      return areDifferent
    } else if (comparisonTemplate[key] === true) {
      areDifferent = objectA[key] !== objectB[key]
      return areDifferent
    } else {
      return false
    }
  })

  return !areDifferent
}

const objA = { 
  a: 1,
  b: {
    a: 21,
    b: 22,
  },
  c: 3,
}

const objB = { 
  a: 1,
  b: {
    a: 21,
    b: 25,
  },
  c: true,
}

// template tells which props to compare
const comparisonTemplateA = {
  a: true,
  b: {
    a: true
  }
}
objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateA)
// returns true

const comparisonTemplateB = {
  a: true,
  c: true
}
// returns false
objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateB)

This will work in the console. Array support could be added if needed

Solution 13 - Javascript

> I need to know if they have difference in one of their nested properties

Other answers provide potentially satisfactory solutions to this problem, but it is sufficiently difficult and common that it looks like there's a very popular package to help solve this issue deep-object-diff.

To use this package you'd need to npm i deep-object-diff then:

const { diff } = require('deep-object-diff');
var a = {};
var b = {};

a.prop1 = 2;
a.prop2 = { prop3: 2 };

b.prop1 = 2;
b.prop2 = { prop3: 3 };

if (!_.isEqual(a, b)) {
  const abDiff = diff(a, b);
  console.log(abDiff);
  /*
  {
    prop2: {
      prop3: 3
    }
  }
  */
}

// or alternatively
const abDiff = diff(a, b);
if(!_.isEmpty(abDiff)) {
  // if a diff exists then they aren't deeply equal
  // perform needed actions with diff...
}

Here's a more detailed case with property deletions directly from their docs:

const lhs = {
  foo: {
    bar: {
      a: ['a', 'b'],
      b: 2,
      c: ['x', 'y'],
      e: 100 // deleted
    }
  },
  buzz: 'world'
};

const rhs = {
  foo: {
    bar: {
      a: ['a'], // index 1 ('b')  deleted
      b: 2, // unchanged
      c: ['x', 'y', 'z'], // 'z' added
      d: 'Hello, world!' // added
    }
  },
  buzz: 'fizz' // updated
};

console.log(diff(lhs, rhs)); // =>
/*
{
  foo: {
    bar: {
      a: {
        '1': undefined
      },
      c: {
        '2': 'z'
      },
      d: 'Hello, world!',
      e: undefined
    }
  },
  buzz: 'fizz'
}
*/

For implementation details and other usage info, refer to that repo.

Solution 14 - Javascript

I know this doesn't directly answer the OP's question but I was led here by searching for how to remove lodash. So hopefully this helps someone else in a similar position as me.

Credit goes to @JohanPersson. I built off of that answer to implement comparing deeply nested values and getting the key reference to the diffs

getObjectDiff = (obj1, obj2) => {
    const obj1Props = Object.keys(obj1);
    const obj2Props = Object.keys(obj2);

    const keysWithDiffValue = obj1Props.reduce((keysWithDiffValueAccumulator, key) => {
      const propExistsOnObj2 = obj2.hasOwnProperty(key);
      const hasNestedValue = obj1[key] instanceof Object && obj2[key] instanceof Object;
      const keyValuePairBetweenBothObjectsIsEqual = obj1[key] === obj2[key];

      if (!propExistsOnObj2) {
        keysWithDiffValueAccumulator.push(key);
      } else if (hasNestedValue) {
        const keyIndex = keysWithDiffValueAccumulator.indexOf(key);
        if (keyIndex >= 0) {
          keysWithDiffValueAccumulator.splice(keyIndex, 1);
        }
        const nestedDiffs = getObjectDiff(obj1[key], obj2[key]);
        for (let diff of nestedDiffs) {
          keysWithDiffValueAccumulator.push(`${key}.${diff}`);
        }
      } else if (keyValuePairBetweenBothObjectsIsEqual) {
        const equalValueKeyIndex = keysWithDiffValueAccumulator.indexOf(key);
        keysWithDiffValueAccumulator.splice(equalValueKeyIndex, 1);
      }
      return keysWithDiffValueAccumulator;
    }, obj2Props);

    return keysWithDiffValue;
  }
const obj1 = {a0: {a1: {a2: {a3: 'Im here'}}}};
const obj2 = {a0: {a1: {a2: {a3: 'Not here', b3: 'some'}}}};
console.log('final', getObjectDiff(obj1, obj2));

Solution 15 - Javascript

Without use of lodash/underscore, I have written this code and is working fine for me for a deep comparison of object1 with object2

function getObjectDiff(a, b) {
	var diffObj = {};
	if (Array.isArray(a)) {
		a.forEach(function(elem, index) {
			if (!Array.isArray(diffObj)) {
				diffObj = [];
			}
			diffObj[index] = getObjectDiff(elem, (b || [])[index]);
		});
	} else if (a != null && typeof a == 'object') {
		Object.keys(a).forEach(function(key) {
			if (Array.isArray(a[key])) {
				var arr = getObjectDiff(a[key], b[key]);
				if (!Array.isArray(arr)) {
					arr = [];
				}
				arr.forEach(function(elem, index) {
					if (!Array.isArray(diffObj[key])) {
						diffObj[key] = [];
					}
					diffObj[key][index] = elem;
				});
			} else if (typeof a[key] == 'object') {
				diffObj[key] = getObjectDiff(a[key], b[key]);
			} else if (a[key] != (b || {})[key]) {
				diffObj[key] = a[key];
			} else if (a[key] == (b || {})[key]) {
				delete a[key];
			}
		});
	}
	Object.keys(diffObj).forEach(function(key) {
		if (typeof diffObj[key] == 'object' && JSON.stringify(diffObj[key]) == '{}') {
			delete diffObj[key];
		}
	});
	return diffObj;
}


Solution 16 - Javascript

This is my solution to the problem

const _ = require('lodash');

var objects = [{ 'x': 1, 'y': 2, 'z':3, a:{b:1, c:2, d:{n:0}}, p:[1, 2, 3]  }, { 'x': 2, 'y': 1, z:3, a:{b:2, c:2,d:{n:1}}, p:[1,3], m:3  }];

const diffFn=(a,b, path='')=>_.reduce(a, function(result, value, key) {

    if(_.isObjectLike(value)){
      if(_.isEqual(value, b[key])){
        return result;
      }else{

return result.concat(diffFn(value, b[key], path?(`${path}.${key}`):key))
      }
    }else{
return _.isEqual(value, b[key]) ?
        result : result.concat(path?(`${path}.${key}`):key);
    }
    
}, []);

const diffKeys1=diffFn(objects[0], objects[1])
const diffKeys2=diffFn(objects[1], objects[0])
const diffKeys=_.union(diffKeys1, diffKeys2)
const res={};

_.forEach(diffKeys, (key)=>_.assign(res, {[key]:{ old: _.get(objects[0], key), new:_.get(objects[1], key)} }))

res
/*
Returns
{
  x: { old: 1, new: 2 },
  y: { old: 2, new: 1 },
  'a.b': { old: 1, new: 2 },
  'a.d.n': { old: 0, new: 1 },
  'p.1': { old: 2, new: 3 },
  'p.2': { old: 3, new: undefined },
  m: { old: undefined, new: 3 }
}
*/

Solution 17 - Javascript

There have been many answers posted but for those curious to avoid writing any code to calculate difference between two objects having any type of structure there is actually a library to do this. Lodash isEqual only returns true or false it doesn't return any information about the changed properties. https://www.npmjs.com/package/deep-diff

its returns full detail of differences between two objects

import DeepDiff from 'deep-diff';
let a = {...} //some object
let b = {...} //some object 
var differences = DeepDiff.diff(a, b);

Similar question has also been asked in this thread https://stackoverflow.com/questions/44651746/getting-the-difference-between-2-json-objects

Solution 18 - Javascript

Completing the answer from Adam Boduch, this one takes into differences in properties

const differenceOfKeys = (...objects) =>
  _.difference(...objects.map(obj => Object.keys(obj)));
const differenceObj = (a, b) => 
  _.reduce(a, (result, value, key) => (
    _.isEqual(value, b[key]) ? result : [...result, key]
  ), differenceOfKeys(b, a));

Solution 19 - Javascript

If you need only key comparison:

 _.reduce(a, function(result, value, key) {
     return b[key] === undefined ? key : []
  }, []);

Solution 20 - Javascript

We had this requirement on getting the delta between two json updates for tracking database updates. Maybe someone else can find this helpful.

https://gist.github.com/jp6rt/7fcb6907e159d7851c8d59840b669e3d

const {
  isObject,
  isEqual,
  transform,
  has,
  merge,
} = require('lodash');
const assert = require('assert');

/**
 * Perform a symmetric comparison on JSON object.
 * @param {*} baseObj - The base object to be used for comparison against the withObj.
 * @param {*} withObj - The withObject parameter is used as the comparison on the base object.
 * @param {*} invert  - Because this is a symmetric comparison. Some values in the with object
 *                      that doesn't exist on the base will be lost in translation.
 *                      You can execute again the function again with the parameters interchanged.
 *                      However you will lose the reference if the value is from the base or with
 *                      object if you intended to do an assymetric comparison.
 *                      Setting this to true will do make sure the reference is not lost.
 * @returns           - The returned object will label the result of the comparison with the
 *                      value from base and with object.
 */
const diffSym = (baseObj, withObj, invert = false) => transform(baseObj, (result, value, key) => {
  if (isEqual(value, withObj[key])
    && has(withObj, key)) {
    return;
  }

  if (isObject(value)
    && isObject(withObj[key])
    && !Array.isArray(value)) {
    result[key] = diffSym(value, withObj[key], invert);
    return;
  }

  if (!invert) {
    result[key] = {
      base: value,
      with: withObj[key],
    };
    return;
  }

  if (invert) {
    result[key] = {
      base: withObj[key],
      with: value,
    };
  }
});

/**
 * Perform a assymmetric comparison on JSON object.
 * @param {*} baseObj - The base object to be used for comparison against the withObj.
 * @param {*} withObj - The withObject parameter is used as the comparison on the base object.
 * @returns           - The returned object will label the values with
 *                      reference to the base and with object.
 */
const diffJSON = (baseObj, withObj) => {
  // Deep clone the objects so we don't update the reference objects.
  const baseObjClone = JSON.parse(JSON.stringify(baseObj));
  const withObjClone = JSON.parse(JSON.stringify(withObj));

  const beforeDelta = diffSym(baseObjClone, withObjClone);
  const afterDelta = diffSym(withObjClone, baseObjClone, true);

  return merge(afterDelta, beforeDelta);
};

// By Example:

const beforeDataObj = {
  a: 1,
  c: { d: 2, f: 3 },
  g: 4,
  h: 5,
};
const afterDataObj = {
  a: 2,
  b: 3,
  c: { d: 1, e: 1 },
  h: 5,
};

const delta = diffJSON(beforeDataObj, afterDataObj);

// Assert expected result.
assert(isEqual(delta, {
  a: { base: 1, with: 2 },
  b: { base: undefined, with: 3 },
  c: {
    d: { base: 2, with: 1 },
    e: { base: undefined, with: 1 },
    f: { base: 3, with: undefined },
  },
  g: { base: 4, with: undefined },
}));

Solution 21 - Javascript

var isEqual = function(f,s) {
  if (f === s) return true;

  if (Array.isArray(f)&&Array.isArray(s)) {
    return isEqual(f.sort(), s.sort());
  }
  if (_.isObject(f)) {
    return isEqual(f, s);
  }
  return _.isEqual(f, s);
};

Solution 22 - Javascript

this was based on @JLavoie, using lodash

let differences = function (newObj, oldObj) {
      return _.reduce(newObj, function (result, value, key) {
        if (!_.isEqual(value, oldObj[key])) {
          if (_.isArray(value)) {
            result[key] = []
            _.forEach(value, function (innerObjFrom1, index) {
              if (_.isNil(oldObj[key][index])) {
                result[key].push(innerObjFrom1)
              } else {
                let changes = differences(innerObjFrom1, oldObj[key][index])
                if (!_.isEmpty(changes)) {
                  result[key].push(changes)
                }
              }
            })
          } else if (_.isObject(value)) {
            result[key] = differences(value, oldObj[key])
          } else {
            result[key] = value
          }
        }
        return result
      }, {})
    }

https://jsfiddle.net/EmilianoBarboza/0g0sn3b9/8/

Solution 23 - Javascript

To build upon Sridhar Gudimela's answer, here it is updated in a way that uses TypeScript:

///  U T I L S

interface LooseObjectInterface {
  [key: string]: any;
};

type inputOptions = LooseObjectInterface | any[];



///  E X P O R T

export const objectCompare = (objectA: inputOptions, objectB: inputOptions): LooseObjectInterface => {
  let diffObj: LooseObjectInterface = {};

  switch(true) {
    case (Array.isArray(objectA)):
      objectA.forEach((elem: any, index: number) => {
        if (!Array.isArray(diffObj))
          diffObj = [];

        diffObj[index] = objectCompare(elem, (objectB || [])[index]);
      });

      break;

    case (objectA !== null && typeof objectA === "object"):
      Object.keys(objectA).forEach((key: any) => {
        if (Array.isArray(objectA[key])) {
          let arr = objectCompare(objectA[key], objectB[key]);

          if (!Array.isArray(arr))
            arr = [];

          arr.forEach((elem: any, index: number) => {
            if (!Array.isArray(diffObj[key]))
              diffObj[key] = [];

            diffObj[key][index] = elem;
          });
        } else if (typeof objectA[key] === "object")
          diffObj[key] = objectCompare(objectA[key], objectB[key]);
        else if (objectA[key] !== (objectB || {})[key])
          diffObj[key] = objectA[key];
        else if (objectA[key] === (objectB || {})[key])
          delete objectA[key];
      });

      break;

    default:
      break;
  }

  Object.keys(diffObj).forEach((key: any) => {
    if (typeof diffObj[key] === "object" && JSON.stringify(diffObj[key]) === "{}")
      delete diffObj[key];
  });

  return diffObj;
};

EDIT: My original answer used Flow, hence the downvotes (I assume, or maybe because my answer didn't use Lodash...however, having an answer to a similar problem can't hurt).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJLavoieView Question on Stackoverflow
Solution 1 - JavascriptJLavoieView Answer on Stackoverflow
Solution 2 - JavascriptAdam BoduchView Answer on Stackoverflow
Solution 3 - JavascriptJohan PerssonView Answer on Stackoverflow
Solution 4 - JavascriptRashad SalehView Answer on Stackoverflow
Solution 5 - JavascriptDPGView Answer on Stackoverflow
Solution 6 - JavascriptSantiago BendavidView Answer on Stackoverflow
Solution 7 - JavascriptblikblumView Answer on Stackoverflow
Solution 8 - JavascriptAlirezaView Answer on Stackoverflow
Solution 9 - Javascriptuser46748View Answer on Stackoverflow
Solution 10 - Javascriptjmoore255View Answer on Stackoverflow
Solution 11 - JavascriptDeminetixView Answer on Stackoverflow
Solution 12 - JavascriptPawelView Answer on Stackoverflow
Solution 13 - JavascriptdracstaxiView Answer on Stackoverflow
Solution 14 - JavascriptDevin LedesmaView Answer on Stackoverflow
Solution 15 - JavascriptSridhar GudimelaView Answer on Stackoverflow
Solution 16 - JavascriptToni BeverinView Answer on Stackoverflow
Solution 17 - JavascriptNabeel Hussain ShahView Answer on Stackoverflow
Solution 18 - JavascriptRicardo FreitasView Answer on Stackoverflow
Solution 19 - JavascriptgdmView Answer on Stackoverflow
Solution 20 - Javascriptjp6rtView Answer on Stackoverflow
Solution 21 - JavascriptCrusaderView Answer on Stackoverflow
Solution 22 - JavascriptEmiliano BarbozaView Answer on Stackoverflow
Solution 23 - JavascriptNetOperator WibbyView Answer on Stackoverflow