Skip to content

Commit dee3d12

Browse files
authored
Merge pull request #332 from Vedavyasa21/add-deep-merge-exercise
feat: Add Deep-Merge Nested Objects Coding Exercise
2 parents dcbc82e + 3aa8ca1 commit dee3d12

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
Deep-Merge Two Nested Objects (with Circular References)
2+
Challenge:
3+
Implement a deep-merge function that combines two nested JavaScript objects while safely handling circular references. The function must be immutable and able to merge objects, arrays, and nested structures.
4+
5+
Problem Description:
6+
Deep merging means recursively combining the properties of two objects. When both inputs contain nested objects, arrays, or shared references, this becomes more complex.
7+
8+
Your task is to:
9+
Merge all properties from both objects
10+
Recursively merge nested objects
11+
Merge arrays by index
12+
Avoid infinite recursion when circular references exist
13+
Return a new, immutable object
14+
Ensure values from the second object override the first when conflicts occur
15+
Real-World Use Case
16+
State management libraries (Redux, Zustand, Immer)
17+
Complex configuration merging
18+
Deep cloning with overrides
19+
Normalizing API responses
20+
Merging schema definitions
21+
22+
Example:
23+
Input
24+
const a = {
25+
x: 1,
26+
y: { z: 2 }
27+
};
28+
a.self = a; // circular reference
29+
30+
const b = {
31+
y: { k: 20 },
32+
m: 100
33+
};
34+
b.loop = b; // circular reference
35+
36+
const result = deepMerge(a, b);
37+
38+
Output
39+
{
40+
x: 1,
41+
y: { z: 2, k: 20 },
42+
m: 100,
43+
self: [circular],
44+
loop: [circular]
45+
}
46+
47+
Requirements:
48+
Must deep-merge nested objects and arrays
49+
Must detect and safely handle circular references
50+
Must not mutate inputs
51+
When both objects contain the same key:
52+
If both values are objects → recursively merge
53+
If both are arrays → merge by index
54+
Otherwise → value from second object overrides
55+
Must create a brand-new object as the output
56+
57+
Key Concepts:
58+
Recursion
59+
WeakMap to track visited nodes
60+
Object and array cloning
61+
Circular reference detection
62+
Immutability principles
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Exercise: Deep-Merge Two Nested Objects (with Circular References)
2+
3+
## Problem Statement
4+
5+
Write a function **`deepMerge(objA, objB)`** that deeply merges two JavaScript objects **without mutating** either of them.
6+
The function must correctly merge nested structures and safely handle circular references.
7+
8+
---
9+
10+
## Rules
11+
12+
1. If a key exists only in one object → copy that value.
13+
2. If a key exists in both objects:
14+
- If both values are **plain objects**, recursively merge them.
15+
- If both values are **arrays**, merge by index (element-wise).
16+
- Otherwise, the value from **`objB`** overrides the value from **`objA`**.
17+
3. Handle **circular references** using a tracking mechanism like **WeakMap**.
18+
4. Always return a **new**, immutable object.
19+
5. Must preserve structure even when circular references appear on both inputs.
20+
21+
---
22+
23+
## Example
24+
25+
```js
26+
const a = {
27+
x: { y: 1 },
28+
arr: [1, 2]
29+
};
30+
a.self = a; // circular
31+
32+
const b = {
33+
x: { z: 2 },
34+
arr: [3],
35+
extra: true
36+
};
37+
b.loop = b; // circular
38+
39+
const result = deepMerge(a, b);
40+
41+
/*
42+
Expected structure:
43+
44+
{
45+
x: { y: 1, z: 2 },
46+
arr: [1, 3], // merged by index
47+
extra: true,
48+
self: <circular>,
49+
loop: <circular>
50+
}
51+
*/
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Deeply merges two objects while safely handling circular references.
3+
* Returns a new object without mutating the originals.
4+
*
5+
* @param {Object|Array} obj1
6+
* @param {Object|Array} obj2
7+
* @returns {Object|Array}
8+
*/
9+
function deepMerge(obj1, obj2, visited = new WeakMap()) {
10+
// If obj2 is primitive, return it directly
11+
if (obj2 === null || typeof obj2 !== "object") {
12+
return obj2;
13+
}
14+
15+
// Detect circular reference
16+
if (visited.has(obj2)) {
17+
return visited.get(obj2);
18+
}
19+
20+
// Determine output type
21+
const output = Array.isArray(obj1) ? [...obj1] :
22+
Array.isArray(obj2) ? [...obj2] :
23+
{ ...obj1 };
24+
25+
visited.set(obj2, output);
26+
27+
for (const key in obj2) {
28+
if (!obj2.hasOwnProperty(key)) continue;
29+
30+
const val1 = obj1 ? obj1[key] : undefined;
31+
const val2 = obj2[key];
32+
33+
if (val1 !== undefined && typeof val1 === "object" && typeof val2 === "object") {
34+
output[key] = deepMerge(val1, val2, visited);
35+
} else {
36+
output[key] = val2;
37+
}
38+
}
39+
40+
return output;
41+
}
42+
43+
// Example usage
44+
const a = { x: 1, y: { z: 2 } };
45+
a.self = a;
46+
47+
const b = { y: { k: 20 }, m: 100 };
48+
b.loop = b;
49+
50+
console.log(deepMerge(a, b));

0 commit comments

Comments
 (0)