Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions coding-exercise/deep-merge-nested-objects/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
Deep-Merge Two Nested Objects (with Circular References)
Challenge:
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.

Problem Description:
Deep merging means recursively combining the properties of two objects. When both inputs contain nested objects, arrays, or shared references, this becomes more complex.

Your task is to:
Merge all properties from both objects
Recursively merge nested objects
Merge arrays by index
Avoid infinite recursion when circular references exist
Return a new, immutable object
Ensure values from the second object override the first when conflicts occur
Real-World Use Case
State management libraries (Redux, Zustand, Immer)
Complex configuration merging
Deep cloning with overrides
Normalizing API responses
Merging schema definitions

Example:
Input
const a = {
x: 1,
y: { z: 2 }
};
a.self = a; // circular reference

const b = {
y: { k: 20 },
m: 100
};
b.loop = b; // circular reference

const result = deepMerge(a, b);

Output
{
x: 1,
y: { z: 2, k: 20 },
m: 100,
self: [circular],
loop: [circular]
}

Requirements:
Must deep-merge nested objects and arrays
Must detect and safely handle circular references
Must not mutate inputs
When both objects contain the same key:
If both values are objects → recursively merge
If both are arrays → merge by index
Otherwise → value from second object overrides
Must create a brand-new object as the output

Key Concepts:
Recursion
WeakMap to track visited nodes
Object and array cloning
Circular reference detection
Immutability principles
51 changes: 51 additions & 0 deletions coding-exercise/deep-merge-nested-objects/question.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Exercise: Deep-Merge Two Nested Objects (with Circular References)

## Problem Statement

Write a function **`deepMerge(objA, objB)`** that deeply merges two JavaScript objects **without mutating** either of them.
The function must correctly merge nested structures and safely handle circular references.

---

## Rules

1. If a key exists only in one object → copy that value.
2. If a key exists in both objects:
- If both values are **plain objects**, recursively merge them.
- If both values are **arrays**, merge by index (element-wise).
- Otherwise, the value from **`objB`** overrides the value from **`objA`**.
3. Handle **circular references** using a tracking mechanism like **WeakMap**.
4. Always return a **new**, immutable object.
5. Must preserve structure even when circular references appear on both inputs.

---

## Example

```js
const a = {
x: { y: 1 },
arr: [1, 2]
};
a.self = a; // circular

const b = {
x: { z: 2 },
arr: [3],
extra: true
};
b.loop = b; // circular

const result = deepMerge(a, b);

/*
Expected structure:

{
x: { y: 1, z: 2 },
arr: [1, 3], // merged by index
extra: true,
self: <circular>,
loop: <circular>
}
*/
50 changes: 50 additions & 0 deletions coding-exercise/deep-merge-nested-objects/solution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Deeply merges two objects while safely handling circular references.
* Returns a new object without mutating the originals.
*
* @param {Object|Array} obj1
* @param {Object|Array} obj2
* @returns {Object|Array}
*/
function deepMerge(obj1, obj2, visited = new WeakMap()) {
// If obj2 is primitive, return it directly
if (obj2 === null || typeof obj2 !== "object") {
return obj2;
}

// Detect circular reference
if (visited.has(obj2)) {
return visited.get(obj2);
}

// Determine output type
const output = Array.isArray(obj1) ? [...obj1] :
Array.isArray(obj2) ? [...obj2] :
{ ...obj1 };

visited.set(obj2, output);

for (const key in obj2) {
if (!obj2.hasOwnProperty(key)) continue;

const val1 = obj1 ? obj1[key] : undefined;
const val2 = obj2[key];

if (val1 !== undefined && typeof val1 === "object" && typeof val2 === "object") {
output[key] = deepMerge(val1, val2, visited);
} else {
output[key] = val2;
}
}

return output;
}

// Example usage
const a = { x: 1, y: { z: 2 } };
a.self = a;

const b = { y: { k: 20 }, m: 100 };
b.loop = b;

console.log(deepMerge(a, b));