Coding Challenge - #11: Deep Object Merge

Write a function deepMerge(target, source) that recursively merges all own enumerable properties from source into target and returns the mutated target. If both objects contain a property whose value is a plain object, merge those objects recursively rather than overwriting.

function deepMerge(target, source) {
  // your code here
}

Rules:

  • Mutate and return the target object.
  • Non-object values or arrays should be overwritten by the source value.
  • Must handle nested objects of arbitrary depth.

Post your solution as a reply. Answer goes up in about a day.

This feels like something @senocular or @krilnon can do in their sleep!

It is fun to see how different people approach these kinds of problems.

Short and concrete wins because teams use it under pressure.

Bookmarked

function deepMerge(target, source) {

for (const key of Object.keys(source)) {

const sourceValue = source\[key\];



if (

  sourceValue !== null &&

  typeof sourceValue === "object" &&

  !Array.isArray(sourceValue) &&

  target\[key\] !== null &&

  typeof target\[key\] === "object" &&

  !Array.isArray(target\[key\])

) {

  deepMerge(target\[key\], sourceValue);

} else {

  target\[key\] = sourceValue;

}

}

return target;

}

This is clean, @emmawalter5. The explicit !Array.isArray checks are crucial for avoiding unexpected behavior with array references.

This looks like a solid approach for deep merging objects. It handles the recursive merging of plain objects and overwrites non-object values as required.

Ngl this is basically how I handle config files in Unreal Engine projects, just gotta make sure the recursion depth doesn’t get wild.

yo that’s a good point about recursion depth. I’ve definitely seen some config files get out of hand.

Challenge solution: The deepMerge function needs to recursively merge properties, handling nested objects by merging them and overwriting non-object values or arrays.

One way to do it:

function deepMerge(target, source) {
  for (const key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (typeof target[key] === 'object' && target[key] !== null && !Array.isArray(target[key]) &&
          typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
        deepMerge(target[key], source[key]);
      } else {
        target[key] = source[key];
      }
    }
  }
  return target;
}

Why:
This solution iterates over the source object’s own enumerable properties. For each property, it checks if both the target and source values are plain objects (not null and not arrays). If they are, it recursively calls deepMerge to merge those nested objects. Otherwise, it overwrites the target property with the source property’s value, handling non-object values and arrays as specified.


Got it: @emmawalter5 :trophy:

First-answer leaderboard

  1. @kirupa - 6 (firsts) :trophy:
  2. @Apexcodes - 3 (firsts)
  3. @adnanahmed - 2 (firsts)
  4. @emmawalter5 - 2 (firsts)

This is a solid approach, especially the explicit checks for plain objects. It prevents unexpected behavior with arrays or other complex types.

The explicit checks are crucial for security.

What happens when someone intentionally passes in a non-plain object to try and mess with the merge logic?

Here’s a concise solution that follows the rules, including treating arrays as values to overwrite:

function deepMerge(target, source) {
  for (const key of Object.keys(source)) {
    const value = source[key];

    if (
      value &&
      typeof value === "object" &&
      !Array.isArray(value) &&
      target[key] &&
      typeof target[key] === "object" &&
      !Array.isArray(target[key])
    ) {
      deepMerge(target[key], value);
    } else {
      target[key] = value;
    }
  }

  return target;
}

For example:

const target = {
  name: "John",
  settings: {
    theme: "light",
    notifications: true
  }
};

const source = {
  settings: {
    theme: "dark"
  },
  age: 25
};

console.log(deepMerge(target, source));

This recursively merges plain objects, while arrays and other values are replaced by the source.

Interesting approach to handling the recursion and array overwrites. We’ll see how it stacks up when the solution is posted later today.

I’m always curious how people handle circular references in these deep merge challenges. that’s usually where things get tricky.

Okay so the circular reference thing is why I always pass a WeakSet or Map to track visited objects. otherwise you just blow the stack.