Skip to content

Commit

Permalink
Fixed #6977 - Refactor deepEquals implementation of ObjectUtils
Browse files Browse the repository at this point in the history
  • Loading branch information
cagataycivici committed Dec 11, 2018
1 parent 468c0f3 commit d2d4a29
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 50 deletions.
13 changes: 4 additions & 9 deletions src/app/components/utils/objectutils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,18 @@ describe('ObjectUtils Suite', () => {

it('Should check if nested objects are equal', () => {
const arr = [1, 2, [3, 4]];
expect(objectUtils.equals(arr, Object.assign({}, arr))).toBe(true);
expect(objectUtils.equals(arr, [1, 2, [3, 4]])).toBe(true);

const arr2 = [1, 2, [3, 4, 5]];
expect(objectUtils.equals(arr, arr2)).toBe(false);

const obj = {a: 1, b: {c: 3, d: 4}};
expect(objectUtils.equals(obj, Object.assign({}, obj))).toBe(true);

const obj2 = {a: 1, b: {c: 3, d: 5}};
expect(objectUtils.equals(obj, obj2)).toBe(false);
});

it('Should not cause stack overflow comparing recursive objects', () => {
const obj1 = {p: null};
const obj2 = {p: null};
obj1['p'] = obj1;
obj2['p'] = obj2;
expect(objectUtils.equals(obj1, obj2)).toBe(false);
});

it('Should be able to compare frozen nested objects', () => {
const obj1 = {a: 1, b: {c: 3, d: 4}};
const obj2 = {a: 1, b: {c: 3, d: 4}};
Expand Down
77 changes: 36 additions & 41 deletions src/app/components/utils/objectutils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,59 +11,54 @@ export class ObjectUtils {
return this.equalsByValue(obj1, obj2);
}

public equalsByValue(obj1: any, obj2: any, visited?: any[]): boolean {
if (obj1 == null && obj2 == null) {
return true;
}
if (obj1 == null || obj2 == null) {
return false;
}
public equalsByValue(obj1: any, obj2: any): boolean {
if (obj1 === obj2) return true;

if (obj1 && obj2 && typeof obj1 == 'object' && typeof obj2 == 'object') {
var arrA = Array.isArray(obj1)
, arrB = Array.isArray(obj2)
, i
, length
, key;

if (arrA && arrB) {
length = obj1.length;
if (length != obj2.length) return false;
for (i = length; i-- !== 0;)
if (!this.equalsByValue(obj1[i], obj2[i])) return false;
return true;
}

if (obj1 == obj2) {
return true;
}
if (arrA != arrB) return false;

if (obj1 instanceof Date && obj2 instanceof Date) {
return obj1.getTime() == obj2.getTime();
}
var dateA = obj1 instanceof Date
, dateB = obj2 instanceof Date;
if (dateA != dateB) return false;
if (dateA && dateB) return obj1.getTime() == obj2.getTime();

if (typeof obj1 == 'object' && typeof obj2 == 'object') {
if (visited) {
if (visited.indexOf(obj1) !== -1) return false;
} else {
visited = [];
}
visited.push(obj1);
var regexpA = obj1 instanceof RegExp
, regexpB = obj2 instanceof RegExp;
if (regexpA != regexpB) return false;
if (regexpA && regexpB) return obj1.toString() == obj2.toString();

for (var p in obj1) {
if (obj1.hasOwnProperty(p) !== obj2.hasOwnProperty(p)) {
return false;
}

switch (typeof (obj1[p])) {
case 'object':
if (!this.equalsByValue(obj1[p], obj2[p], visited)) return false;
break;
var keys = Object.keys(obj1);
length = keys.length;

case 'function':
if (typeof (obj2[p]) == 'undefined' || (p != 'compare' && obj1[p].toString() != obj2[p].toString())) return false;
break;
if (length !== Object.keys(obj2).length)
return false;

default:
if (obj1[p] != obj2[p]) return false;
break;
}
}
for (i = length; i-- !== 0;)
if (!Object.prototype.hasOwnProperty.call(obj2, keys[i])) return false;

for (var p in obj2) {
if (typeof (obj1[p]) == 'undefined') return false;
for (i = length; i-- !== 0;) {
key = keys[i];
if (!this.equalsByValue(obj1[key], obj2[key])) return false;
}

delete obj1._$visited;
return true;
}

return false;
return obj1 !== obj1 && obj2 !== obj2;
}

public resolveFieldData(data: any, field: any): any {
Expand Down

0 comments on commit d2d4a29

Please sign in to comment.