| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 |
- /**
- * Is deep equal.
- * https://github.com/pinglu85/BFEdevSolutions/blob/main/Coding-Problems/69.implement-deep-equal-isEqual.md
- *
- * @param {*} firstValue
- * @param {*} secondValue
- * @returns {boolean}
- */
- export function isDeepEqual(firstValue, secondValue) {
- const cached = new WeakMap();
- const compare = (a, b) => {
- // Check if one of the two inputs is primitive by using typeof
- // operator; since the typeof primitive null is object, check
- // if one of the inputs is equal to null. If one of the two
- // inputs is primitive, then I can compare them by reference.
- if (a === null || b === null) return a === b;
- if (typeof a !== 'object' || typeof b !== 'object') return a === b;
- // Check if the data type of the two inputs are the same,
- // both are arrays or objects. If they are not, return false.
- const dataTypeA = Array.isArray(a) ? 'array' : 'object';
- const dataTypeB = Array.isArray(b) ? 'array' : 'object';
- if (dataTypeA !== dataTypeB) return false;
- // Use Object.keys and Object.getOwnPropertySymbols to get
- // all of enumerable and not-inherited properties of the two
- // inputs. Compare their size respectively, if one of them
- // is not equal, return false.
- const keysA = Object.keys(a);
- const keysB = Object.keys(b);
- if (keysA.length !== keysB.length) return false;
- const symbolsA = Object.getOwnPropertySymbols(a);
- const symbolsB = Object.getOwnPropertySymbols(b);
- if (symbolsA.length !== symbolsB.length) return false;
- // To handle the circular reference, initialize a WeakMap
- // that is going to keep track of the objects or arrays
- // that have been seen, in which each key is an object
- // or an array and each value is a set of objects or arrays,
- // that have been compared to that object or array.
- let setForA = cached.get(a);
- if (setForA == null) {
- setForA = new Set();
- cached.set(a, setForA);
- } else if (setForA.has(b)) {
- return true;
- }
- setForA.add(b);
- let setForB = cached.get(b);
- if (setForB == null) {
- setForB = new Set();
- cached.set(b, setForB);
- } else if (setForB.has(a)) {
- return true;
- }
- setForB.add(a);
- // Compare the property names and the values. Loop through
- // all the properties of the first input data, check if
- // the property name also exist in the second input data,
- // if not, return false; otherwise recursively compare
- // the property value.
- const propertyNamesA = [...keysA, ...symbolsA];
- for (const propertyNameA of propertyNamesA) {
- if (!Object.prototype.hasOwnProperty.call(b, propertyNameA)) return false;
- const propertyValueA = a[propertyNameA];
- const propertyValueB = b[propertyNameA];
- if (!compare(propertyValueA, propertyValueB)) return false;
- }
- // If we get out of the loop without
- // returning false, return true.
- return true;
- };
- return compare(firstValue, secondValue);
- }
|