diffObjectsDeep
Recursively computes the deep difference between two objects. For each differing key, returns a tuple [aValue, bValue]
. Supports nested objects and arrays.
1/**
2 * Recursively computes the deep difference between two objects.
3 * For each differing key, returns a tuple [aValue, bValue].
4 * Supports nested objects and arrays.
5 *
6 * @param a - First object to compare.
7 * @param b - Second object to compare.
8 * @returns A new object with differences at each path.
9 */
10export function diffObjectsDeep(
11 a: Record<string, any>,
12 b: Record<string, any>
13): Record<string, any> {
14 const result: Record<string, any> = {};
15
16 const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
17
18 for (const key of keys) {
19 const valA = a[key];
20 const valB = b[key];
21
22 const bothAreObjects =
23 valA &&
24 valB &&
25 typeof valA === 'object' &&
26 typeof valB === 'object' &&
27 valA.constructor === valB.constructor;
28
29 if (bothAreObjects && !Array.isArray(valA)) {
30 const nestedDiff = diffObjectsDeep(valA, valB);
31 if (Object.keys(nestedDiff).length > 0) {
32 result[key] = nestedDiff;
33 }
34 } else if (Array.isArray(valA) && Array.isArray(valB)) {
35 if (valA.length !== valB.length || valA.some((v, i) => v !== valB[i])) {
36 result[key] = [valA, valB];
37 }
38 } else if (valA !== valB) {
39 result[key] = [valA, valB];
40 }
41 }
42
43 return result;
44}
Recursive Deep Comparison
Traverses nested structures (including plain objects and arrays) to detect differences at any depth.
Detailed Change Representation
Returns
[aValue, bValue]
tuples for differing leaf nodes, giving clear visibility into both sides of the change.Handles Arrays and Objects Intelligently
Differentiates between array and object comparison logic for more accurate diffing.
Minimal Output for Changed Paths
Excludes identical values and returns only the paths where actual changes occurred, making the output compact and relevant.
Constructor-Aware Comparisons
Avoids false positives by checking that compared values share the same constructor (e.g., not mixing objects with arrays or class instances).
Tests | Examples
1test('diffObjectsDeep - top-level difference', () => {
2 const a = { name: 'Alice', age: 30 };
3 const b = { name: 'Bob', age: 30 };
4 expect(diffObjectsDeep(a, b)).toEqual({ name: ['Alice', 'Bob'] });
5});
6
7test('diffObjectsDeep - nested object difference', () => {
8 const a = { user: { id: 1, name: 'Alice' } };
9 const b = { user: { id: 2, name: 'Alice' } };
10 expect(diffObjectsDeep(a, b)).toEqual({ user: { id: [1, 2] } });
11});
12
13test('diffObjectsDeep - deep nesting', () => {
14 const a = { settings: { theme: { dark: true } } };
15 const b = { settings: { theme: { dark: false } } };
16 expect(diffObjectsDeep(a, b)).toEqual({
17 settings: { theme: { dark: [true, false] } },
18 });
19});
20
21test('diffObjectsDeep - array difference', () => {
22 const a = { tags: ['a', 'b'] };
23 const b = { tags: ['a', 'c'] };
24 expect(diffObjectsDeep(a, b)).toEqual({ tags: [['a', 'b'], ['a', 'c']] });
25});
26
27test('diffObjectsDeep - detects missing keys', () => {
28 const a = { name: 'Alice' };
29 const b = {};
30 expect(diffObjectsDeep(a, b)).toEqual({ name: ['Alice', undefined] });
31});
32
33test('diffObjectsDeep - equal deeply nested objects', () => {
34 const a = { config: { features: { x: true, y: false } } };
35 const b = { config: { features: { x: true, y: false } } };
36 expect(diffObjectsDeep(a, b)).toEqual({});
37});
Common Use Cases
Deep State Comparison
Detect differences between two deeply nested state trees in React, Redux, or similar architectures.
Audit Logging and History Tracking
Log or snapshot fine-grained changes in structured data (e.g., documents, user profiles, settings).
Form Change Detection
Identify which specific fields have been altered across complex or nested form inputs.
Patch or Sync Generation
Generate targeted update payloads for syncing with APIs or databases without resending unchanged data.
Testing and Assertions
Compare expected vs. actual deeply nested structures in unit or integration tests to pinpoint mismatches.