Page MenuHomePhabricator

D7753.id27258.diff
No OneTemporary

D7753.id27258.diff

diff --git a/lib/utils/objects.js b/lib/utils/objects.js
--- a/lib/utils/objects.js
+++ b/lib/utils/objects.js
@@ -4,9 +4,11 @@
import invariant from 'invariant';
import _difference from 'lodash/fp/difference.js';
import _isEqual from 'lodash/fp/isEqual.js';
+import _isPlainObject from 'lodash/fp/isPlainObject.js';
import stringHash from 'string-hash';
-type Map<K, T> = { +[key: K]: T };
+type ObjectMap<K, T> = { +[key: K]: T };
+type NestedObjectMap<K, T> = { +[key: K]: T | NestedObjectMap<K, T> };
function findMaximumDepth(obj: Object): ?{ path: string, depth: number } {
let longestPath = null;
@@ -37,7 +39,7 @@
return { path: longestPath, depth: longestDepth };
}
-function values<K, T>(map: Map<K, T>): T[] {
+function values<K, T>(map: ObjectMap<K, T>): T[] {
return Object.values
? // https://github.com/facebook/flow/issues/2221
// $FlowFixMe - Object.values currently does not have good flow support
@@ -45,6 +47,21 @@
: Object.keys(map).map((key: K): T => map[key]);
}
+function keys<K, T>(map: ObjectMap<K, T>): K[] {
+ return Object.keys(map);
+}
+
+function assignValueWithKey<K, T>(
+ obj: NestedObjectMap<K, T>,
+ key: K,
+ value: T | NestedObjectMap<K, T>,
+): NestedObjectMap<K, T> {
+ return {
+ ...obj,
+ ...Object.fromEntries([[key, value]]),
+ };
+}
+
function hash(obj: ?Object): number {
if (!obj) {
return -1;
@@ -52,9 +69,36 @@
return stringHash(stableStringify(obj));
}
+// returns an object with properties from obj1 not included in obj2
+function deepDiff<K, T>(
+ obj1: NestedObjectMap<K, T>,
+ obj2: NestedObjectMap<K, T>,
+): NestedObjectMap<K, T> {
+ let diff: NestedObjectMap<K, T> = {};
+ keys(obj1).forEach((key: K) => {
+ if (_isEqual(obj1[key], obj2[key])) {
+ return;
+ }
+
+ if (!_isPlainObject(obj1[key]) || !_isPlainObject(obj2[key])) {
+ diff = assignValueWithKey(diff, key, obj1[key]);
+ return;
+ }
+
+ const nestedObj1: ObjectMap<K, T> = (obj1[key]: any);
+ const nestedObj2: ObjectMap<K, T> = (obj2[key]: any);
+
+ const nestedDiff = deepDiff(nestedObj1, nestedObj2);
+ if (Object.keys(nestedDiff).length > 0) {
+ diff = assignValueWithKey(diff, key, nestedDiff);
+ }
+ });
+ return diff;
+}
+
function assertObjectsAreEqual<K, T>(
- processedObject: Map<K, T>,
- expectedObject: Map<K, T>,
+ processedObject: ObjectMap<K, T>,
+ expectedObject: ObjectMap<K, T>,
message: string,
) {
const processedObjectKeys = Object.keys(processedObject);
@@ -75,4 +119,4 @@
);
}
-export { findMaximumDepth, values, hash, assertObjectsAreEqual };
+export { findMaximumDepth, values, hash, assertObjectsAreEqual, deepDiff };
diff --git a/lib/utils/objects.test.js b/lib/utils/objects.test.js
new file mode 100644
--- /dev/null
+++ b/lib/utils/objects.test.js
@@ -0,0 +1,103 @@
+// @flow
+
+import { deepDiff } from './objects.js';
+
+describe('deepDiff tests', () => {
+ it('should return an empty object if the objects are identical', () => {
+ const obj1 = {
+ key1: 'value1',
+ key2: { foo: 'bar' },
+ };
+ const obj2 = {
+ key1: 'value1',
+ key2: { foo: 'bar' },
+ };
+ const diff = deepDiff(obj1, obj2);
+ expect(diff).toEqual({});
+ });
+
+ it('should return the differences between two objects', () => {
+ const obj1 = {
+ key1: 'value1',
+ key2: { prop: 'a' },
+ };
+ const obj2 = {
+ key1: 'value2',
+ key2: { prop: 'b' },
+ };
+ const diff = deepDiff(obj1, obj2);
+ expect(diff).toEqual({
+ key1: 'value1',
+ key2: {
+ prop: 'a',
+ },
+ });
+ });
+
+ it('should handle objects nested in objects', () => {
+ const obj1 = {
+ key1: 'value1',
+ key2: { prop: 'a', nested: { xyz: 123 } },
+ };
+ const obj2 = {
+ key1: 'value1',
+ key2: { prop: 'a', nested: { xyz: 124 } },
+ };
+ const diff = deepDiff(obj1, obj2);
+ expect(diff).toEqual({
+ key2: {
+ nested: {
+ xyz: 123,
+ },
+ },
+ });
+ });
+
+ it('should handle nested objects with null and undefined values', () => {
+ const obj1 = {
+ key1: null,
+ key2: { prop: undefined },
+ };
+ const obj2 = {
+ key1: undefined,
+ key2: { prop: null },
+ };
+ const diff = deepDiff(obj1, obj2);
+ expect(diff).toEqual({
+ key1: null,
+ key2: {
+ prop: undefined,
+ },
+ });
+ });
+
+ it('should handle objects with different value types', () => {
+ const obj1 = {
+ key1: 'value1',
+ key2: 123,
+ };
+ const obj2 = {
+ key1: 'value1',
+ key2: '123',
+ };
+ const diff = deepDiff(obj1, obj2);
+ expect(diff).toEqual({
+ key2: 123,
+ });
+ });
+
+ it('should handle objects with array value types', () => {
+ const obj1 = {
+ key1: ['value1'],
+ key2: ['a', 1],
+ };
+ const obj2 = {
+ key1: ['value1'],
+ key2: ['a', 2],
+ };
+ const diff = deepDiff(obj1, obj2);
+ expect(diff).toEqual({
+ key2: ['a', 1],
+ });
+ });
+});

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 5, 3:24 PM (11 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2616408
Default Alt Text
D7753.id27258.diff (4 KB)

Event Timeline