39

I am working on migration of angular 1 project to angular 2 . In angular 1 project I was using angular.equals for object comparison angular.equals($ctrl.obj1, $ctrl.newObj); , I searched online for equivalent method in angular 2 but could not find any matching result.

Dmehro
  • 1,269
  • 1
  • 16
  • 29
  • 2
    There is nothing like that in Angular2 AFAIK. There is only http://stackoverflow.com/questions/35903941/object-equality-in-typescript – Günter Zöchbauer Nov 14 '16 at 20:55
  • Possible duplicate of [How to determine equality for two JavaScript objects?](http://stackoverflow.com/questions/201183/how-to-determine-equality-for-two-javascript-objects) – Fiddles Nov 15 '16 at 01:41
  • @Fiddles this question is more specific to Angular 2 and link which you posted is more generic java-script solution – Dmehro Nov 15 '16 at 20:35

6 Answers6

25

@Günter Yes you are right there is no equivalent in angular2 . While searching more I found third party library lodash which will do same job as angular.equals and syntax is same as angular one and this library solves my problem

Code example from lodash documentation

var object = { 'a': 1 };
var other = { 'a': 1 };
 
_.isEqual(object, other);
// => true
 
object === other;
// => false
Sibiraj
  • 4,486
  • 7
  • 33
  • 57
Dmehro
  • 1,269
  • 1
  • 16
  • 29
22

I rewrote Ariels answer (thank you!) to be TSLINT-friendly. You can also save some continues by using else if, but I think this is more clear. Maybe someone else needs it too:

export function deepEquals(x, y) {
  if (x === y) {
    return true; // if both x and y are null or undefined and exactly the same
  } else if (!(x instanceof Object) || !(y instanceof Object)) {
    return false; // if they are not strictly equal, they both need to be Objects
  } else if (x.constructor !== y.constructor) {
    // they must have the exact same prototype chain, the closest we can do is
    // test their constructor.
    return false;
  } else {
    for (const p in x) {
      if (!x.hasOwnProperty(p)) {
        continue; // other properties were tested using x.constructor === y.constructor
      }
      if (!y.hasOwnProperty(p)) {
        return false; // allows to compare x[ p ] and y[ p ] when set to undefined
      }
      if (x[p] === y[p]) {
        continue; // if they have the same strict value or identity then they are equal
      }
      if (typeof (x[p]) !== 'object') {
        return false; // Numbers, Strings, Functions, Booleans must be strictly equal
      }
      if (!deepEquals(x[p], y[p])) {
        return false;
      }
    }
    for (const p in y) {
      if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
        return false;
      }
    }
    return true;
  }
}
Phil
  • 7,065
  • 8
  • 49
  • 91
  • 1
    Most IDEs should have an "auto-format on save" option that will change code to match your linting settings automatically. On VSCode it's `"editor.formatOnSave": true`, in conjunction with `prettier` plugin (and possibly `tslint`/ `eslint`) – RJFalconer Jul 18 '17 at 12:44
  • 1
    When I tried using this routine it returned a false positive when the two objects have a member that's a JavaScript Date type that aren't the same date. Expect that two JS Dates should be compared using `getTime()` – FirstVertex Apr 27 '18 at 17:40
  • This is 90% of the way there, but it doesn't handle reference loops. For example, if `foo.ref = foo` and `bar.ref = bar` this method will get stuck in an infinite loop since they both have the same structure of `{ ref: {...} }` but are referencing different instances. – Acorath Nov 30 '20 at 18:20
9

Instead of writing a function to iterate through the objects, you could just use JSON.stringify and compare the two strings?

Example:

var obj1 = {
  title: 'title1',
  tags: []
}

var obj2 = {
  title: 'title1',
  tags: ['r']
}


console.log(JSON.stringify(obj1));
console.log(JSON.stringify(obj2));


console.log(JSON.stringify(obj1) === JSON.stringify(obj2));
Stephen Kaiser
  • 1,545
  • 14
  • 10
  • 7
    This works for most cases, but can fail for semantically-identical cases. E.g re-ordering the members from your example http://jsfiddle.net/9d8gjy9e/ – RJFalconer Jul 18 '17 at 12:41
  • This worked for me, as I was only comparing simple objects – Sylvan D Ash Sep 20 '17 at 14:44
  • 2
    as the order of members matters, such comparison is not ok – marbug Oct 31 '17 at 16:05
  • This is nice quick solution if you do not mind the edge cases where it might fail. Also, it must be enhanced if [JSON.stringify fails due to circular references](https://stackoverflow.com/questions/11616630/json-stringify-avoid-typeerror-converting-circular-structure-to-json). – Alexei - check Codidact Aug 29 '18 at 13:19
  • This shouldn't be relied upon for any reason (except maybe a polyfill for a very old IE including an on-fail full property/value comparison for performance reasons) – TamusJRoyce Sep 30 '20 at 18:22
4

In Angular 2 you should use pure JavaScript/TypeScript for that so you can add this method to some service

private static equals(x, y) {
    if (x === y)
        return true;
    // if both x and y are null or undefined and exactly the same
    if (!(x instanceof Object) || !(y instanceof Object))
        return false;
    // if they are not strictly equal, they both need to be Objects
    if (x.constructor !== y.constructor)
        return false;
    // they must have the exact same prototype chain, the closest we can do is
    // test there constructor.

    let p;
    for (p in x) {
        if (!x.hasOwnProperty(p))
            continue;
        // other properties were tested using x.constructor === y.constructor
        if (!y.hasOwnProperty(p))
            return false;
        // allows to compare x[ p ] and y[ p ] when set to undefined
        if (x[p] === y[p])
            continue;
        // if they have the same strict value or identity then they are equal
        if (typeof (x[p]) !== "object")
            return false;
        // Numbers, Strings, Functions, Booleans must be strictly equal
        if (!RXBox.equals(x[p], y[p]))
            return false;
    }
    for (p in y) {
        if (y.hasOwnProperty(p) && !x.hasOwnProperty(p))
            return false;
    }
    return true;
}
Ariel Henryson
  • 1,316
  • 1
  • 10
  • 13
  • 2
    What's RXBox ?? – Louis Mar 26 '17 at 00:00
  • https://medium.com/@ariel.henryson/angular-2-state-store-strategy-using-rxbox-62eaf3251e90#.uta4kv9lm RXBox -> It's a library I wrote to handle store inside Angular app the function here is part of the library. – Ariel Henryson Mar 26 '17 at 08:27
  • 1
    To clarify, `RXBox.equals` is the qualified name of this method; it's recursive. – James May 19 '17 at 21:37
2

You could just copy the original source code from angularjs for the angular.equals function. Usage: equals(obj1, obj2);

var toString = Object.prototype.toString;

function isDefined(value) {return typeof value !== 'undefined';}
function isFunction(value) {return typeof value === 'function';}
function createMap() {
  return Object.create(null);
}
function isWindow(obj) {
  return obj && obj.window === obj;
}
function isScope(obj) {
  return obj && obj.$evalAsync && obj.$watch;
}
function isRegExp(value) {
  return toString.call(value) === '[object RegExp]';
}
function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }
function isDate(value) {
  return toString.call(value) === '[object Date]';
}
function isArray(arr) {
  return Array.isArray(arr) || arr instanceof Array;
}
function equals(o1, o2) {
  if (o1 === o2) return true;
  if (o1 === null || o2 === null) return false;
  // eslint-disable-next-line no-self-compare
  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
  if (t1 === t2 && t1 === 'object') {
    if (isArray(o1)) {
      if (!isArray(o2)) return false;
      if ((length = o1.length) === o2.length) {
        for (key = 0; key < length; key++) {
          if (!equals(o1[key], o2[key])) return false;
        }
        return true;
      }
    } else if (isDate(o1)) {
      if (!isDate(o2)) return false;
      return simpleCompare(o1.getTime(), o2.getTime());
    } else if (isRegExp(o1)) {
      if (!isRegExp(o2)) return false;
      return o1.toString() === o2.toString();
    } else {
      if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
        isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
      keySet = createMap();
      for (key in o1) {
        if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
        if (!equals(o1[key], o2[key])) return false;
        keySet[key] = true;
      }
      for (key in o2) {
        if (!(key in keySet) &&
            key.charAt(0) !== '$' &&
            isDefined(o2[key]) &&
            !isFunction(o2[key])) return false;
      }
      return true;
    }
  }
  return false;
}
Shane
  • 303
  • 2
  • 9
1
a = { name: 'me' }
b = { name: 'me' }
a == b // false
a === b // false
JSON.stringify(a) == JSON.stringify(b) // true
JSON.stringify(a) === JSON.stringify(b) // true
Mr.Zon
  • 195
  • 7
  • 2
    This fails for objects where properties are defined in different order. See [his demo here](https://jsfiddle.net/wilt/w72ax3tL/) – Wilt Feb 24 '20 at 17:57
  • property order is not guaranteed. Object are essentially hashtables, which shouldn't guarantee order. – TamusJRoyce Sep 30 '20 at 18:19