Skip to content

Instantly share code, notes, and snippets.

@iamakulov
Last active March 22, 2024 10:00
Show Gist options
  • Save iamakulov/5f1e74cdcffc8a0bb3d2278dd54d8328 to your computer and use it in GitHub Desktop.
Save iamakulov/5f1e74cdcffc8a0bb3d2278dd54d8328 to your computer and use it in GitHub Desktop.

deepCompare performs a deep comparison of two objects. It automatically unwraps JS Maps, Sets, and ImmutableJS objects.

Example

deepCompare(left, right)

// Will output:
// {
//  isEqual: false,
//  reason: "left[...] !== right[...]",
//  path: ".variables.d4d7eb27b6ed4f3cb061eb56e4bd9b7b.evaluatedExpression.__DEFAULT_SCENARIO__._lookup.0._lookup.0.distribution.histogram.data.0.0.0",
//  left: 65656595.47231187,
//  right: 55
// }
// 
// or:
// {
//   isEqual: true
// }

Return value

deepCompare returns as soon as it finds the first difference. It doesn’t collect all the differences.

The return object is either

type ReturnObject = {
  isEqual: true;
}

or

type ReturnObject = {
  isEqual: false;
  reason: string;
  // Path to the first field where the comparison found the difference
  path: string;
  // If the comparison found two properties not === to each other,
  // it will return their values
  left?: any; 
  right?: any;
}
function deepCompare(a, b, path = "") {
// Convert JS maps to objects
if (a instanceof Map) a = Object.fromEntries(a);
if (b instanceof Map) b = Object.fromEntries(b);
// Convert JS sets to arrays
if (a instanceof Set) a = Array.from(a);
if (b instanceof Set) b = Array.from(a);
// Convert Immutable data to JS data
if (a?.toJS) a = a.toJS();
if (b?.toJS) b = b.toJS();
if (a === b) {
return { isEqual: true };
}
if (a == null || typeof a !== "object" || b == null || typeof b !== "object") {
return { isEqual: false, reason: "left[...] !== right[...]", path, left: a, right: b };
}
let aOwnProperties = Object.getOwnPropertyNames(a);
let bOwnProperties = Object.getOwnPropertyNames(b);
for (let prop of aOwnProperties) {
if (!bOwnProperties.includes(prop)) {
return { isEqual: false, reason: `prop ${prop} is present in left but not in right`, path };
}
}
for (let prop of bOwnProperties) {
if (!aOwnProperties.includes(prop)) {
return { isEqual: false, reason: `prop ${prop} is present in right but not in left`, path };
}
let isDeepEqual = deepEqual(a[prop], b[prop], path + "." + prop);
if (!isDeepEqual.isEqual) {
return isDeepEqual;
}
}
return { isEqual: true }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment