Compare nested objects in JavaScript and return keys equality

You could use reduce to build new object and another get method to get nested props from other object by string and compare it to current prop value in first object.

const obj1 = { prop1: 1, prop2: "foo", prop3: { prop4: 2, prop5: "bar" } }
const obj2 = { prop1: 3, prop2: "foo", prop3: { prop4: 2, prop5: "foobar" } }

function get(obj, path) {
  return path.split('.').reduce((r, e) => {
    if (!r) return r
    else return r[e] || undefined
  }, obj)
}

function compare(a, b, prev = "") {
  return Object.keys(a).reduce((r, e) => {
    const path = prev + (prev ? '.' + e : e);
    const value = a[e] === get(b, path);
    r[e] = typeof a[e] === 'object' ? compare(a[e], b, path) : value
    return r;
  }, {})
}

const result = compare(obj1, obj2);
console.log(result)

To compare all properties of both objects you could create extra function that will perform loop by both objects.

const obj1 = {"prop1":1,"prop2":"foo","prop3":{"prop4":2,"prop5":"bar"},"prop7":{"prop9":{"prop10":"foo"}}}
const obj2 = {"prop1":3,"prop2":"foo","prop3":{"prop4":2,"prop5":"foobar"},"prop6":"new","prop7":{"foo":"foo","bar":{"baz":"baz"}}}

function get(obj, path) {
  return path.split('.').reduce((r, e) => {
    if (!r) return r;
    else return r[e] || undefined;
  }, obj);
}

function isEmpty(o) {
  if (typeof o !== 'object') return true;
  else return !Object.keys(o).length;
}

function build(a, b, o = null, prev = '') {
  return Object.keys(a).reduce(
    (r, e) => {
      const path = prev + (prev ? '.' + e : e);
      const bObj = get(b, path);
      const value = a[e] === bObj;

      if (typeof a[e] === 'object') {
        if (isEmpty(a[e]) && isEmpty(bObj)) {
          if (e in r) r[e] = r[e];
          else r[e] = true;
        } else if (!bObj && isEmpty(a[e])) {
          r[e] = value;
        } else {
          r[e] = build(a[e], b, r[e], path);
        }
      } else {
        r[e] = value;
      }
      return r;
    },
    o ? o : {}
  );
}

function compare(a, b) {
  const o = build(a, b);
  return build(b, a, o);
}

const result = compare(obj1, obj2);
console.log(result)

You could create a merged object which will have keys of both object. Loop through this object and compare the values for both obj1 and obj2 for each key. If the property is an object, recursively compare the properties. This will work for any level of nesting. Since the properties could be missing from either of the objects, default parameter = {} is added.

const obj1={prop1:1,prop2:"foo",prop3:{prop4:2,prop5:"bar"},prop7:{pro8:"only in 1"}},
      obj2={prop1:3,prop2:"foo",prop3:{prop4:2,prop5:"foobar"}, prop6: "only in 2"};
    
const isObject = val => typeof val === 'object' && val // required for "null" comparison

function compare(obj1 = {}, obj2 = {}) {
  const output = {},
        merged = { ...obj1, ...obj2 }; // has properties of both

  for (const key in merged) {
    const value1 = obj1[key],
          value2 = obj2[key];

    if (isObject(value1) || isObject(value2))
      output[key] = compare(value1, value2) // recursively call
    else
      output[key] = value1 === value2
  }
  
  return output;
}

console.log(compare(obj1, obj2))

You could iterate all keys and check the nested objects if both values are objects.

const isObject = v => v && typeof v === 'object';

function getDifference(a, b) {
    return Object.assign(...Array.from(
        new Set([...Object.keys(a), ...Object.keys(b)]),
        k => ({ [k]: isObject(a[k]) && isObject(b[k])
            ? getDifference(a[k], b[k])
            : a[k] === b[k]
        })
    ));
}

var obj1 = { prop1: 1, prop2: "foo", prop3: { prop4: 2, prop5: "bar" } },
    obj2 = { prop1: 3, prop2: "foo", prop3: { prop4: 2, prop5: "foobar" }, prop6: "new" };

console.log(getDifference(obj1, obj2));

Tags:

Javascript