Hone logo
Hone
Problems

JavaScript Object Merging Challenge

You're building a system that requires combining configuration settings from multiple sources. Often, you'll have a default configuration and then a user-defined configuration that might override or add to those defaults. This challenge will test your ability to effectively merge two JavaScript objects.

Problem Description

Your task is to create a JavaScript function that takes two objects as input and returns a new object representing the merged result. The merging process should prioritize properties from the second object, meaning if a property exists in both objects, the value from the second object should be used in the merged output. The function should also handle nested objects by performing a deep merge.

Key Requirements:

  • The function should accept exactly two arguments, obj1 and obj2, both of which are expected to be JavaScript objects.
  • The function must return a new object. The original objects should not be modified.
  • If a property exists in both obj1 and obj2, the value from obj2 should take precedence.
  • If a property exists only in obj1, it should be included in the merged object.
  • If a property exists only in obj2, it should be included in the merged object.
  • The merge should be deep. If a property's value in both objects is itself an object, these nested objects should also be merged recursively. Non-object values will be overwritten as described above.

Expected Behavior:

  • Primitive values (strings, numbers, booleans, null, undefined) from obj2 will overwrite those from obj1.
  • Arrays are treated as primitive values and will be overwritten entirely by the array from obj2 if the property exists in both.
  • Nested objects will be merged recursively.

Edge Cases to Consider:

  • What happens if one or both of the input objects are empty?
  • What happens if the input arguments are not objects (e.g., null, undefined, primitives)? The function should handle these gracefully.

Examples

Example 1:

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { b: { d: 3 }, e: 4 };

Output:

{ a: 1, b: { c: 2, d: 3 }, e: 4 }

Explanation:

  • a: 1 is from obj1 as it's not in obj2.
  • b is an object in both. We recursively merge obj1.b and obj2.b. obj2.b has d: 3, so it's included. obj1.b has c: 2, which is not in obj2.b, so it's also included.
  • e: 4 is from obj2 as it's not in obj1.

Example 2:

const obj1 = { name: "Default", settings: { theme: "light", fontSize: 12 } };
const obj2 = { settings: { fontSize: 14, language: "en" }, isActive: true };

Output:

{ name: "Default", settings: { theme: "light", fontSize: 14, language: "en" }, isActive: true }

Explanation:

  • name: "Default" is from obj1.
  • settings is an object in both. obj2.settings.fontSize (14) overwrites obj1.settings.fontSize (12). obj1.settings.theme ("light") is kept as it's not in obj2.settings. obj2.settings.language ("en") is added.
  • isActive: true is from obj2.

Example 3: Array Handling

const obj1 = { data: [1, 2], config: { items: [10, 20] } };
const obj2 = { data: [3, 4], config: { items: [30] } };

Output:

{ data: [3, 4], config: { items: [30] } }

Explanation:

  • data is an array in both. The array from obj2 ([3, 4]) completely replaces the array from obj1.
  • config is an object in both. The items property within config is also an array. The array from obj2 ([30]) replaces the array from obj1.

Constraints

  • The input objects can have any level of nesting.
  • The function should be efficient for typical object sizes.
  • Input arguments should ideally be objects, but the function should not throw errors if null or undefined are passed; it should return an object based on the non-null/undefined input.

Notes

  • Consider how to check if a value is an object and not null.
  • Think about how to handle circular references if they were a concern (though for this challenge, you can assume no circular references).
  • A good approach might involve recursion.
Loading editor...
javascript