Hone logo
Hone
Problems

Deep Merge Objects in JavaScript

Deep merging objects is a common task when dealing with configuration files, state management, or any scenario where you need to combine multiple objects into a single, unified object, handling nested structures correctly. This challenge asks you to implement a function that recursively merges objects, ensuring that nested objects are also merged instead of being overwritten.

Problem Description

You are tasked with creating a JavaScript function called deepMerge that takes two or more objects as arguments and returns a new object that is the deep merge of all the input objects. The function should recursively merge nested objects, meaning if an object contains other objects as properties, those inner objects should also be merged. If a key exists in multiple input objects, the value from the later object in the argument list should take precedence. The original input objects should not be modified.

Key Requirements:

  • Recursion: The function must recursively merge nested objects.
  • Precedence: Values from later objects in the argument list should overwrite values from earlier objects.
  • Immutability: The original input objects must remain unchanged. A new object should be created and returned.
  • Handles Non-Objects: If a value is not an object (e.g., a string, number, boolean, null, or undefined), it should be directly copied to the merged object.
  • Handles Arrays: Arrays should be treated as objects for merging purposes. If an array key exists in multiple objects, the array from the later object should overwrite the earlier one.

Expected Behavior:

The deepMerge function should return a new object containing all the properties from the input objects, with values from later objects overriding values from earlier objects when keys collide. Nested objects should be recursively merged.

Examples

Example 1:

Input: deepMerge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
Output: { a: 1, b: { c: 2, d: 3 }, e: 4 }
Explanation: The 'b' property is overwritten with the nested object from the second argument. The 'e' property is added from the second argument.

Example 2:

Input: deepMerge({ a: 1, b: 2 }, { c: 3, d: 4 })
Output: { a: 1, b: 2, c: 3, d: 4 }
Explanation: No keys collide, so all properties are simply added to the new object.

Example 3:

Input: deepMerge({ a: 1, b: { c: 2 } }, { a: 3, b: { d: 4 } })
Output: { a: 3, b: { c: 2, d: 4 } }
Explanation: The 'a' property is overwritten. The 'b' property is recursively merged, adding 'd' to the existing object.

Example 4: (Edge Case - Null/Undefined Values)

Input: deepMerge({ a: null }, { a: 1 })
Output: { a: 1 }
Explanation: Null value is overwritten by the number 1.

Constraints

  • The function should accept two or more objects as arguments.
  • Input objects can be empty.
  • The function should handle circular references gracefully (avoid infinite loops). While a full circular reference detection isn't required, the function shouldn't crash if it encounters one. A simple check to prevent re-merging the same object is sufficient.
  • Performance: The function should be reasonably efficient for objects with a moderate number of properties (up to 1000). While extreme optimization isn't required, avoid unnecessarily complex operations.
  • Input objects will only contain primitive values, other objects, or arrays. No functions or other complex data types are expected.

Notes

  • Consider using recursion to traverse the object structure.
  • Remember to create a new object to store the merged result to avoid modifying the original objects.
  • Think about how to handle cases where a key exists in multiple objects.
  • A simple check to prevent re-merging the same object can help avoid infinite loops in case of circular references. This can be done by keeping track of objects already visited during the merge process.
Loading editor...
javascript