How to Concatenate Arrays with Lodash mergeWith

In JavaScript development, combining deeply nested objects often requires combining arrays rather than overwriting their contents. Lodash's standard _.merge method recursively merges objects, but it overwrites array elements by their index position instead of appending them. To achieve array concatenation during a merge, Lodash provides _.mergeWith, which accepts a customizer function to dictate exactly how specific data types—such as arrays—are combined.

The Default Array Behavior of _.merge

By default, _.merge treats arrays like indexed objects. If the target object has an array [1, 2] and the source object has [3], the merged result will be [3, 2]. The value at index 0 is overwritten, while the value at index 1 remains untouched. This behavior is usually undesirable when working with lists, where the expected behavior is typically appending new items to the existing array.

How _.mergeWith Works

The _.mergeWith method works identically to _.merge, but it accepts an additional customizer callback function as its final argument:

_.mergeWith(object, sources, [customizer])

The customizer function receives several arguments: (objValue, srcValue, key, object, source, stack).

When the customizer returns a value, _.mergeWith uses that returned value for the merge. If the customizer returns undefined, the method falls back to Lodash's default recursive merge strategy. This makes handling arrays straightforward: you only handle array types in the customizer and return undefined for everything else.

Implementing Array Concatenation

To concatenate arrays instead of overwriting their indices, check if the target property value is an array. If it is, combine it with the source value using JavaScript's native Array.prototype.concat or Lodash's _.concat:

const _ = require('lodash');

function customizer(objValue, srcValue) {
  if (_.isArray(objValue)) {
    return objValue.concat(srcValue);
  }
}

const object1 = {
  user: 'Alice',
  roles: ['admin'],
  settings: { theme: 'dark' }
};

const object2 = {
  roles: ['editor', 'viewer'],
  settings: { notifications: true }
};

const result = _.mergeWith({}, object1, object2, customizer);

console.log(result);
// Output:
// {
//   user: 'Alice',
//   roles: ['admin', 'editor', 'viewer'],
//   settings: { theme: 'dark', notifications: true }
// }

In this example, the roles arrays are concatenated into a single list, while the nested settings object continues to merge normally because the customizer returns undefined for non-array values.

Handling Duplicates and Unique Values

If the goal is to concatenate arrays while removing duplicate items, combine the concatenation logic with _.uniq or JavaScript's Set:

function uniqueConcatCustomizer(objValue, srcValue) {
  if (_.isArray(objValue)) {
    return _.uniq(objValue.concat(srcValue));
  }
}

const sourceA = { tags: ['javascript', 'web'] };
const sourceB = { tags: ['web', 'backend'] };

const merged = _.mergeWith({}, sourceA, sourceB, uniqueConcatCustomizer);

console.log(merged.tags);
// Output: ['javascript', 'web', 'backend']

By leveraging _.mergeWith and targeting array instances within the customizer function, you retain Lodash's deep-merging capabilities for objects while ensuring arrays are combined according to your application's requirements.