I wrote a little class that is doing what you want, you can test it here.

Only thing that is different from your proposal is that I don't consider

[1,[{c: 1},2,3],{a:'hey'}]

and

[{a:'hey'},1,[3,{c: 1},2]]

to be same, because I think that arrays are not equal if order of their elements is not same. Of course this can be changed if needed. Also this code can be further enhanced to take function as argument that will be used to format diff object in arbitrary way based on passed primitive values (now this job is done by "compareValues" method).

var deepDiffMapper = function () {
  return {
    VALUE_CREATED: 'created',
    VALUE_UPDATED: 'updated',
    VALUE_DELETED: 'deleted',
    VALUE_UNCHANGED: 'unchanged',
    map: function(obj1, obj2) {
      if (this.isFunction(obj1) || this.isFunction(obj2)) {
        throw 'Invalid argument. Function given, object expected.';
      }
      if (this.isValue(obj1) || this.isValue(obj2)) {
        return {
          type: this.compareValues(obj1, obj2),
          data: obj1 === undefined ? obj2 : obj1
        };
      }

      var diff = {};
      for (var key in obj1) {
        if (this.isFunction(obj1[key])) {
          continue;
        }

        var value2 = undefined;
        if (obj2[key] !== undefined) {
          value2 = obj2[key];
        }

        diff[key] = this.map(obj1[key], value2);
      }
      for (var key in obj2) {
        if (this.isFunction(obj2[key]) || diff[key] !== undefined) {
          continue;
        }

        diff[key] = this.map(undefined, obj2[key]);
      }

      return diff;

    },
    compareValues: function (value1, value2) {
      if (value1 === value2) {
        return this.VALUE_UNCHANGED;
      }
      if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
        return this.VALUE_UNCHANGED;
      }
      if (value1 === undefined) {
        return this.VALUE_CREATED;
      }
      if (value2 === undefined) {
        return this.VALUE_DELETED;
      }
      return this.VALUE_UPDATED;
    },
    isFunction: function (x) {
      return Object.prototype.toString.call(x) === '[object Function]';
    },
    isArray: function (x) {
      return Object.prototype.toString.call(x) === '[object Array]';
    },
    isDate: function (x) {
      return Object.prototype.toString.call(x) === '[object Date]';
    },
    isObject: function (x) {
      return Object.prototype.toString.call(x) === '[object Object]';
    },
    isValue: function (x) {
      return !this.isObject(x) && !this.isArray(x);
    }
  }
}();


var result = deepDiffMapper.map({
  a: 'i am unchanged',
  b: 'i am deleted',
  e: {
    a: 1,
    b: false,
    c: null
  },
  f: [1, {
    a: 'same',
    b: [{
      a: 'same'
    }, {
      d: 'delete'
    }]
  }],
  g: new Date('2017.11.25')
}, {
  a: 'i am unchanged',
  c: 'i am created',
  e: {
    a: '1',
    b: '',
    d: 'created'
  },
  f: [{
    a: 'same',
    b: [{
      a: 'same'
    }, {
      c: 'create'
    }]
  }, 1],
  g: new Date('2017.11.25')
});
console.log(result);

Answer from sbgoran on Stack Overflow
Top answer
1 of 16
222

I wrote a little class that is doing what you want, you can test it here.

Only thing that is different from your proposal is that I don't consider

[1,[{c: 1},2,3],{a:'hey'}]

and

[{a:'hey'},1,[3,{c: 1},2]]

to be same, because I think that arrays are not equal if order of their elements is not same. Of course this can be changed if needed. Also this code can be further enhanced to take function as argument that will be used to format diff object in arbitrary way based on passed primitive values (now this job is done by "compareValues" method).

var deepDiffMapper = function () {
  return {
    VALUE_CREATED: 'created',
    VALUE_UPDATED: 'updated',
    VALUE_DELETED: 'deleted',
    VALUE_UNCHANGED: 'unchanged',
    map: function(obj1, obj2) {
      if (this.isFunction(obj1) || this.isFunction(obj2)) {
        throw 'Invalid argument. Function given, object expected.';
      }
      if (this.isValue(obj1) || this.isValue(obj2)) {
        return {
          type: this.compareValues(obj1, obj2),
          data: obj1 === undefined ? obj2 : obj1
        };
      }

      var diff = {};
      for (var key in obj1) {
        if (this.isFunction(obj1[key])) {
          continue;
        }

        var value2 = undefined;
        if (obj2[key] !== undefined) {
          value2 = obj2[key];
        }

        diff[key] = this.map(obj1[key], value2);
      }
      for (var key in obj2) {
        if (this.isFunction(obj2[key]) || diff[key] !== undefined) {
          continue;
        }

        diff[key] = this.map(undefined, obj2[key]);
      }

      return diff;

    },
    compareValues: function (value1, value2) {
      if (value1 === value2) {
        return this.VALUE_UNCHANGED;
      }
      if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
        return this.VALUE_UNCHANGED;
      }
      if (value1 === undefined) {
        return this.VALUE_CREATED;
      }
      if (value2 === undefined) {
        return this.VALUE_DELETED;
      }
      return this.VALUE_UPDATED;
    },
    isFunction: function (x) {
      return Object.prototype.toString.call(x) === '[object Function]';
    },
    isArray: function (x) {
      return Object.prototype.toString.call(x) === '[object Array]';
    },
    isDate: function (x) {
      return Object.prototype.toString.call(x) === '[object Date]';
    },
    isObject: function (x) {
      return Object.prototype.toString.call(x) === '[object Object]';
    },
    isValue: function (x) {
      return !this.isObject(x) && !this.isArray(x);
    }
  }
}();


var result = deepDiffMapper.map({
  a: 'i am unchanged',
  b: 'i am deleted',
  e: {
    a: 1,
    b: false,
    c: null
  },
  f: [1, {
    a: 'same',
    b: [{
      a: 'same'
    }, {
      d: 'delete'
    }]
  }],
  g: new Date('2017.11.25')
}, {
  a: 'i am unchanged',
  c: 'i am created',
  e: {
    a: '1',
    b: '',
    d: 'created'
  },
  f: [{
    a: 'same',
    b: [{
      a: 'same'
    }, {
      c: 'create'
    }]
  }, 1],
  g: new Date('2017.11.25')
});
console.log(result);

2 of 16
117

Using Underscore, a simple diff:

var o1 = {a: 1, b: 2, c: 2},
    o2 = {a: 2, b: 1, c: 2};

_.omit(o1, function(v,k) { return o2[k] === v; })

Results in the parts of o1 that correspond but with different values in o2:

{a: 1, b: 2}

It'd be different for a deep diff:

function diff(a,b) {
    var r = {};
    _.each(a, function(v,k) {
        if(b[k] === v) return;
        // but what if it returns an empty object? still attach?
        r[k] = _.isObject(v)
                ? _.diff(v, b[k])
                : v
            ;
        });
    return r;
}

As pointed out by @Juhana in the comments, the above is only a diff a-->b and not reversible (meaning extra properties in b would be ignored). Use instead a-->b-->a:

(function(_) {
  function deepDiff(a, b, r) {
    _.each(a, function(v, k) {
      // already checked this or equal...
      if (r.hasOwnProperty(k) || b[k] === v) return;
      // but what if it returns an empty object? still attach?
      r[k] = _.isObject(v) ? _.diff(v, b[k]) : v;
    });
  }

  /* the function */
  _.mixin({
    diff: function(a, b) {
      var r = {};
      deepDiff(a, b, r);
      deepDiff(b, a, r);
      return r;
    }
  });
})(_.noConflict());

See http://jsfiddle.net/drzaus/9g5qoxwj/ for full example+tests+mixins

🌐
npm
npmjs.com › package › deep-object-diff
deep-object-diff - npm
November 12, 2022 - detailedDiff(original, updatedObj) returns an object with the added, deleted and updated differences
      » npm install deep-object-diff
    
Published   Nov 12, 2022
Version   1.1.9
Author   Matt Phillips
🌐
JSR
jsr.io › @opentf › obj-diff
@opentf/obj-diff - JSR
You can extend the default diff function using the diffWith function. Now you can compare any object types of your own.
🌐
DEV Community
dev.to › digitaldrreamer › how-to-compare-diff-two-objects-2ocd
How to Compare (diff) two Objects - DEV Community
January 7, 2026 - Surprisingly, this returns false even though both objects have identical content. This happens because JavaScript compares object references, not their values. Both objects point to different locations in memory. A quick way to compare objects is using JSON.stringify:
🌐
GeeksforGeeks
geeksforgeeks.org › javascript › print-difference-between-two-objects-in-javascript
Print Difference between Two Objects in JavaScript ? - GeeksforGeeks
August 5, 2025 - Example: The Function takes two objects (obj1 and obj2) as arguments and returns an object representing the differences between them. It iterates through the keys of both objects, comparing their values.
🌐
GitHub
gist.github.com › Yimiprod › 7ee176597fef230d1451
Deep diff between two object, using lodash · GitHub
I did a small change to deal with an array of objects as a key of the object we wanted to diff: export const deepDiffBetweenObjects = (object, base) => { const changes = (object, base) => { return transform(object, (result, value, key) => { if (!isEqual(value, base[key])) { if (isArray(value)) { result[key] = difference(value, base[key]) } else if (isObject(value) && isObject(base[key])) { result[key] = changes(value, base[key]) } else { result[key] = value } } }) } return changes(object, base) }
🌐
JSON Diff
jsondiff.com
JSON Diff - The semantic JSON compare tool
Validate, format, and compare two JSON documents. See the differences between the objects instead of just the new lines and mixed up properties.
🌐
npm
npmjs.com › search
object diff - npm search
A lightweight javascript diff library for comparing two javascript object nested with supporting matching by value of the object's choosen key name in array. ... eraykose• 1.1.0 • 6 years ago • 4 dependents • MITpublished version 1.1.0, ...
Find elsewhere
🌐
Go Make Things
gomakethings.com › getting-the-differences-between-two-objects-with-vanilla-js
Getting the differences between two objects with vanilla JS | Go Make Things
February 25, 2019 - Next, we need to loop through our first object, and compare each value in it to the matching value in our second object. We’ll create a helper function, compare(), to handle that for us. var diff = function (obj1, obj2) { // Make sure an object to compare is provided if (!obj2 || Object.prototype.toString.call(obj2) !== '[object Object]') { return obj1; } // // Variables // var diffs = {}; var key; // // Compare our objects // // Loop through the first object for (key in obj1) { if (obj1.hasOwnProperty(key)) { compare(obj1[key], obj2[key], key); } } };
🌐
Davidwells
davidwells.io › snippets › get-difference-between-two-objects-javascript
Get the difference between two objects in JavaScript
This is quite handy for determining to do something if a value you care about in an object has changed. const { inspect } = require('util') const transform = require('lodash.transform') const isEqual = require('lodash.isequal') const isArray = require('lodash.isarray') const isObject = require('lodash.isobject') /** * Find difference between two objects * @param {object} origObj - Source object to compare newObj against * @param {object} newObj - New object with potential changes * @return {object} differences */ function difference(origObj, newObj) { function changes(newObj, origObj) { let arrayIndexCounter = 0 return transform(newObj, function (result, value, key) { if (!isEqual(value, origObj[key])) { let resultKey = isArray(origObj) ?
🌐
GitHub
github.com › mattphillips › deep-object-diff
GitHub - mattphillips/deep-object-diff: Deep diffs two objects, including nested structures of arrays and objects, and returns the difference. ❄️
A small library that can deep diff two JavaScript Objects, including nested structures of arrays and objects.
Starred by 1.1K users
Forked by 96 users
Languages   JavaScript
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › Set › difference
Set.prototype.difference() - JavaScript | MDN - Mozilla
July 10, 2025 - js · difference(other) other · A Set object, or set-like object. A new Set object containing elements in this set but not in the other set. In mathematical notation, difference is defined as: A · ∖ · B · = { x · ∊ · A · ∣ · x · ∉ · B · } A\setminus B = \{x\in A\mid x\notin ...
🌐
Vanillajstoolkit
vanillajstoolkit.com › helpers › diff
diff.js | The Vanilla JS Toolkit
/*! * Find the differences between two objects and push to a new object * (c) 2019 Chris Ferdinandi & Jascha Brinkmann, MIT License, https://gomakethings.com & https://twitter.com/jaschaio * @param {Object} obj1 The original object * @param {Object} obj2 The object to compare against it * @return {Object} An object of differences between the two */ var diff = function (obj1, obj2) { // Make sure an object to compare is provided if (!obj2 || Object.prototype.toString.call(obj2) !== '[object Object]') { return obj1; } // // Variables // var diffs = {}; var key; // // Methods // /** * Check if tw
🌐
Syncfusion
syncfusion.com › blogs › javascript › 5 different ways to deep compare javascript objects
5 Different Ways to Deep Compare JavaScript Objects | Syncfusion Blogs
January 15, 2026 - Even though JavaScript does not have an out-of-the-box solution to compare two objects, it has no problem comparing two strings. Therefore, in this method, we convert our two objects into strings using the JSON.stringify() method and compare ...
🌐
HackerNoon
hackernoon.com › introducing-a-new-javascript-library-for-object-diffing-and-patching
Introducing a New JavaScript Library for Object Diffing and Patching | HackerNoon
May 1, 2024 - Open Tech Foundation introduces a new JavaScript library to assist with object diffing and patching. Demos and more are included.
🌐
DEV Community
dev.to › asyncbanana › building-the-fastest-object-and-array-differ-2l6f
Building the fastest object and array differ - DEV Community
January 1, 2022 - Additionally, if you want near Microdiff speeds, you have to use the primary diff function instead of detailedDiff, making it so you do not know the change type. While JSDiff supports object diffing, it is primarily designed for diffing text.
🌐
DEV Community
dev.to › thangaganapathy › the-fast-accurate-javascript-objects-diffing-patching-library-1bdn
🚀 The Fast, Accurate, JavaScript Objects Diffing & Patching Library - DEV Community
May 1, 2024 - The diff result is called patches. It allows us to send fewer data to the backend to apply the patches. The patching is the method used to re-create the modified object at the other end using the original object + Patches (the diff result).
🌐
JSFiddle
jsfiddle.net › sbgoran › kySNu
Generic deep diff between two objects - JSFiddle - Code Playground
We do and so much of JSFiddle was still dependant on it till this day, but since almost all MooTools features are now available in native JS it was high-time to strip it out of the codebase.
🌐
npm
npmjs.com › package › object-diff
object-diff - npm
February 7, 2017 - npm install object-diff · var diff = require('object-diff'); var a = { speed: 4, power: 54, height: undefined, level: 1, }; var b = { speed: 4, // unchanged · power: 22, // changed · level: undefined, // changed · weight: 10, // added · }; diff(a, b); /* { power: 22, level: undefined, weight: 10, } */ // using a custom equality function ·
      » npm install object-diff
    
Published   Feb 07, 2017
Version   0.0.4
Author   Thomas Jensen
🌐
Hacker News
news.ycombinator.com › item
The fastest object diff library in JavaScript | Hacker News
November 7, 2021 - Seems that this particular library is aware of these potential issues (which motivated me to write this comment): https://github.com/AsyncBanana/microdiff/issues/2 · It's a good point about edge cases in diffing. Dynamic value diffing in dynamically-typed languages is an interesting problem ...