Skip to content

Commit

Permalink
refactor custom isEqual to utilise lodash.isequal
Browse files Browse the repository at this point in the history
  • Loading branch information
capaj committed Jun 4, 2019
1 parent 5048b1b commit 3e992f9
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 43 deletions.
20 changes: 19 additions & 1 deletion packages/apollo-utilities/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/apollo-utilities/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@
},
"dependencies": {
"fast-json-stable-stringify": "^2.0.0",
"lodash.isequal": "^4.5.0",
"ts-invariant": "^0.4.0",
"tslib": "^1.9.3"
},
"devDependencies": {
"@types/lodash.isequal": "^4.5.5"
}
}
81 changes: 81 additions & 0 deletions packages/apollo-utilities/src/util/__tests__/isEqual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,85 @@ describe('isEqual', () => {
expect(!isEqual([1, 2, 3], [1, 2, 4])).toBe(true);
delete Array.prototype.foo;
});

describe('comparing objects with circular refs', () => {
// copied with slight modification from lodash test suite
it('should compare objects with circular references', () => {
const object1 = {},
object2 = {};

object1.a = object1;
object2.a = object2;

expect(isEqual(object1, object2)).toBe(true);

object1.b = 0;
object2.b = Object(0);

expect(isEqual(object1, object2)).toBe(true);

object1.c = Object(1);
object2.c = Object(2);

expect(isEqual(object1, object2)).toBe(false);

object1 = { a: 1, b: 2, c: 3 };
object1.b = object1;
object2 = { a: 1, b: { a: 1, b: 2, c: 3 }, c: 3 };

expect(isEqual(object1, object2)).toBe(false);
});

it('should have transitive equivalence for circular references of objects', () => {
const object1 = {},
object2 = { a: object1 },
object3 = { a: object2 };

object1.a = object1;

expect(isEqual(object1, object2)).toBe(true);
expect(isEqual(object2, object3)).toBe(true);
expect(isEqual(object1, object3)).toBe(true);
});

it('should compare objects with multiple circular references', () => {
const array1 = [{}],
array2 = [{}];

(array1[0].a = array1).push(array1);
(array2[0].a = array2).push(array2);

expect(isEqual(array1, array2)).toBe(true);

array1[0].b = 0;
array2[0].b = Object(0);

expect(isEqual(array1, array2)).toBe(true);

array1[0].c = Object(1);
array2[0].c = Object(2);

expect(isEqual(array1, array2)).toBe(false);
});

it('should compare objects with complex circular references', () => {
const object1 = {
foo: { b: { c: { d: {} } } },
bar: { a: 2 },
};

const object2 = {
foo: { b: { c: { d: {} } } },
bar: { a: 2 },
};

object1.foo.b.c.d = object1;
object1.bar.b = object1.foo.b;

object2.foo.b.c.d = object2;
object2.bar.b = object2.foo.b;

expect(isEqual(object1, object2)).toBe(true);
});
});
});
45 changes: 3 additions & 42 deletions packages/apollo-utilities/src/util/isEqual.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,8 @@
import isEqualLodash from 'lodash.isequal';

/**
* Performs a deep equality check on two JavaScript values.
*/
export function isEqual(a: any, b: any): boolean {
// If the two values are strictly equal, we are good.
if (a === b) {
return true;
}
// Dates are equivalent if their time values are equal.
if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
}
// If a and b are both objects, we will compare their properties. This will compare arrays as
// well.
if (
a != null &&
typeof a === 'object' &&
b != null &&
typeof b === 'object'
) {
// Compare all of the keys in `a`. If one of the keys has a different value, or that key does
// not exist in `b` return false immediately.
for (const key in a) {
if (Object.prototype.hasOwnProperty.call(a, key)) {
if (!Object.prototype.hasOwnProperty.call(b, key)) {
return false;
}
if (!isEqual(a[key], b[key])) {
return false;
}
}
}
// Look through all the keys in `b`. If `b` has a key that `a` does not, return false.
for (const key in b) {
if (
Object.prototype.hasOwnProperty.call(b, key) &&
!Object.prototype.hasOwnProperty.call(a, key)
) {
return false;
}
}
// If we made it this far the objects are equal!
return true;
}
// Otherwise the values are not equal.
return false;
return isEqualLodash(a, b);
}

0 comments on commit 3e992f9

Please sign in to comment.