diff --git a/lib/utils/objects.js b/lib/utils/objects.js index d046022e0..1b136c751 100644 --- a/lib/utils/objects.js +++ b/lib/utils/objects.js @@ -1,152 +1,145 @@ // @flow import stableStringify from 'fast-json-stable-stringify'; import invariant from 'invariant'; import _isEqual from 'lodash/fp/isEqual.js'; import _isPlainObject from 'lodash/fp/isPlainObject.js'; import stringHash from 'string-hash'; type ObjectMap = { +[key: K]: T }; type NestedObjectMap = { +[key: K]: T | NestedObjectMap }; function findMaximumDepth(obj: Object): ?{ path: string, depth: number } { let longestPath = null; let longestDepth = null; for (const key in obj) { const value = obj[key]; if (typeof value !== 'object' || !value) { if (!longestDepth) { longestPath = key; longestDepth = 1; } continue; } const childResult = findMaximumDepth(obj[key]); if (!childResult) { continue; } const { path, depth } = childResult; const ourDepth = depth + 1; if (longestDepth === null || ourDepth > longestDepth) { longestPath = `${key}.${path}`; longestDepth = ourDepth; } } if (!longestPath || !longestDepth) { return null; } return { path: longestPath, depth: longestDepth }; } function values(map: ObjectMap): T[] { return Object.values - ? // https://github.com/facebook/flow/issues/2221 - // $FlowFixMe - Object.values currently does not have good flow support - Object.values(map) + ? Object.values(map) : Object.keys(map).map((key: K): T => map[key]); } -function keys(map: ObjectMap): K[] { - return Object.keys(map); -} - function entries(map: ObjectMap): [K, T][] { - // $FlowFixMe - flow treats the values as mixed, but we know that they are T return Object.entries(map); } function assignValueWithKey( obj: NestedObjectMap, key: K, value: T | NestedObjectMap, ): NestedObjectMap { return { ...obj, ...Object.fromEntries([[key, value]]), }; } function hash(obj: ?Object): number { if (!obj) { return -1; } return stringHash(stableStringify(obj)); } // This function doesn't look at the order of the hashes inside of the array // e.g `combineUnorderedHashes([1,2,3]) === combineUnorderedHashes([3,1,2])` // so it should only be used if the hashes include their ordering in them // somehow (e.g. `RawThreadInfo` contains `id`) function combineUnorderedHashes(hashes: $ReadOnlyArray): number { return hashes.reduce((a, v) => a ^ v, 0); } // returns an object with properties from obj1 not included in obj2 function deepDiff( obj1: NestedObjectMap, obj2: NestedObjectMap, ): NestedObjectMap { let diff: NestedObjectMap = {}; - keys(obj1).forEach((key: K) => { + Object.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 = (obj1[key]: any); const nestedObj2: ObjectMap = (obj2[key]: any); const nestedDiff = deepDiff(nestedObj1, nestedObj2); if (Object.keys(nestedDiff).length > 0) { diff = assignValueWithKey(diff, key, nestedDiff); } }); return diff; } function assertObjectsAreEqual( processedObject: ObjectMap, expectedObject: ObjectMap, message: string, -) { +): void { if (_isEqual(processedObject)(expectedObject)) { return; } const dataProcessedButNotExpected = deepDiff(processedObject, expectedObject); const dataExpectedButNotProcessed = deepDiff(expectedObject, processedObject); throw new Error( `${message}: Objects should be equal.` + ` Data processed but not expected:` + ` ${JSON.stringify(dataProcessedButNotExpected)}` + ` Data expected but not processed:` + ` ${JSON.stringify(dataExpectedButNotProcessed)}`, ); } function invertObjectToMap(obj: { +[K]: V }): Map { const invertedMap = new Map(); for (const key of Object.keys(obj)) { invariant( !invertedMap.has(obj[key]), `invertObjectToMap: obj[${key}] is already in invertedMap`, ); invertedMap.set(obj[key], key); } return invertedMap; } export { findMaximumDepth, values, hash, combineUnorderedHashes, assertObjectsAreEqual, deepDiff, entries, invertObjectToMap, };