Combining Settings Objects with Lodash: _.assign or _.merge?

When you're developing a JavaScript component which lets the user provide an object holding some options, you usually need to merge its values with your component's defaults. Settings for which the user didn't provide a value should fall back to a reasonable default.

Suppose we have the following default and user settings:

// Defined within your component
var defaultSettings = {
    strictMode: true,
    formatting: {
        finalNewline: true,
        quotes: "double"
    }
};

// Provided by the developer using your component
var userSettings = {
    formatting: {
        quotes: "single"
    }
};

Note that no value for the strictMode property was specified by the user. That's perfectly fine, we're just going to use the default value of true. The same goes for the finalNewline property, we're falling back to true as well. Only the quotes value changed from its default value of "double" to "single".

In the end, we'd like the merged settings to look like this:

var settings = {
    strictMode: true,
    formatting: {
        finalNewline: true,
        quotes: "single"
    }
};

The lodash library contains two similar functions, _.assign and _.merge, that assign property values of some source object(s) to a target object, effectively merging their properties. Let's take a look at their differences.

Merging Properties Using _.assign

The _.assign function (also called _.extend) accepts as its first parameter a target object to which all properties of the following parameters will be assigned. Values defined by later parameters will overwrite sooner values.

A common pattern is to provide {}, an empty object literal, as the target into which all settings are merged. The default values come first, followed by user-provided settings which overwrite already defined properties.

That sounds perfect for merging settings with default fallback values, does't it? Well, not entirely, as this example shows:

var assignedSettings = _.assign({}, defaultSettings, userSettings);

// {
//     strictMode: true,
//     formatting: {
//         quotes: "single"
//     }
// }

As you can see, the formatting property of the user's settings was assigned as a whole, as the _.assign. name already suggests. The true value of finalNewline, another property of the formatting object, is now lost.

Let's compare this with the behavior of the _.merge function.

Merging Properties Using _.merge

The _.merge function has the same signature as _.assign, but behaves a little differently. Instead of assigning values as a whole, it recursively merges properties that don't hold the value undefined into the target object.

Let's combine the user settings with the default values once more:

var mergedSettings = _.merge({}, defaultSettings, userSettings);

// {
//     strictMode: true,
//     formatting: {
//         finalNewline: true,
//         quotes: "single"
//     }
// }

Much better! Now, the formatting object hasn't been replaced by the partial user definition, but has had its default values merged with the user settings.

So, if you intend to perform a deep merge of user-provided settings with your own default values, use _.merge rather than _.assign.

Use the coupon code LAUNCHDAY for $10 off!

Learn React

7 Comments

Delapouite

Thanks Marius.

What about _.defaults ?

https://lodash.com/docs#defaults

Marius Schulz

@Delapouite: _.defaults works slightly differently than _.merge, but can be used as well to combine multiple settings objects.

The difference is in how undefined values are handled: _.merge recursively copies over values that are not undefined within the source object, while _.defaults only assigns to those properties that are still undefined in the target, thus preventing overwriting existing values.

Dane MacMillan

Using defaultsDeep is more semantically meaningful, given the intent of the merge process. Using merge seems like a carryover, or just plain old habit, from jQuery's extend, which has commonly been used for handling default settings in jQuery plugins. Now that lodash provides a method explicitly for handling defaults, it would make more sense to use it, and shake the old jQuery habit. It would also make the code's meaning more obvious: from just a visual standpoint it makes sense to first have user settings, then fill in any gaps with the defaults on the right, instead of vise versa, and once a a value has been assigned, ignore any future reassignments of it, as any double assignments from a default are almost certainly a mistake.

var mergedSettings = _.defaultsDeep(userSettings, defaultSettings);

Excellent article, by the way. Perhaps you can mention defaults and defaultsDeep when explaining the differences between assign and merge?

Bariali Mahmood

Excellent article, prior to your article; understand those functions some time baffled me. That wouldn't be the case anymore. Really appreciate your time and effort for putting this article up. ;)

Bertrand

Very nice article. Thanks.

Carlos

What about the es2015 spread operator?

Tyler

Above, Dane mentioned _.defaultsDeep. His explanation is correct, except it leaves out on characteristic. It won't overwrite properties from an object set later in the parameter order. I think the intent was to allow that since the example is taking default settings and allowing the user to override them.

I'm currently doing this exact thing right now with _.merge because I need to overwrite properties deep within an object for theming. _.defaultsDeep would not allow this.