Using the Lodash setWith Customizer Function
Lodash's _.setWith method extends the functionality of
_.set by accepting a customizer callback to
control how intermediate objects along a specified path are produced.
This article explains how the customizer function works,
its arguments, and how to use it to guide path generation, such as
forcing plain object creation instead of default arrays or instantiating
specific data structures.
Understanding the
customizer Signature
The _.setWith method follows this syntax:
_.setWith(object, path, value, [customizer])When traversing or creating paths, _.setWith invokes the
customizer function for each unresolved segment along the
path. The callback accepts up to three arguments:
nsValue: The current value of the resolved path segment.key: The key or index of the current path segment.nsObject: The parent object where the property is being assigned.
If the customizer returns undefined,
_.setWith falls back to its default behavior: creating an
array if the next segment is a number or numeric string, and creating a
plain object otherwise. If the customizer returns any other
value, that returned value becomes the intermediate container at that
point in the path.
Preventing Default Array Creation
By default, Lodash creates arrays when intermediate path segments are
integers. A common use case for the customizer is to
prevent this and force plain object creation:
const _ = require('lodash');
const object = {};
// Without customizer: creates an array at key '0'
// _.set(object, '[0][1]', 'a'); // => { '0': [ <1 empty item>, 'a' ] }
// With customizer: force plain objects for numeric keys
_.setWith(object, '[0][1]', 'value', (nsValue) => {
return _.isObject(nsValue) ? nsValue : {};
});
console.log(object);
// Output: { '0': { '1': 'value' } }In this implementation, the customizer checks if
nsValue is already an object. If it is not, it returns a
new object {} instead of allowing Lodash to generate an
array.
Using Custom Constructors and Prototypes
You can also use the customizer to inject custom class
instances or specific prototypes into the hierarchy:
class CustomNode {
constructor() {
this.isCustom = true;
}
}
const tree = {};
_.setWith(tree, 'branch.leaf', 'data', (nsValue) => {
return _.isObject(nsValue) ? nsValue : new CustomNode();
});
console.log(tree.branch instanceof CustomNode);
// Output: true
console.log(tree.branch.leaf);
// Output: 'data'Selective Customization Using Path Keys
Because the key argument is provided, intermediate
objects can be created conditionally based on property names:
const config = {};
_.setWith(config, 'settings.list.0', 'item', (nsValue, key) => {
if (key === 'list') {
return [];
}
return _.isObject(nsValue) ? nsValue : {};
});
console.log(Array.isArray(config.settings.list));
// Output: trueBy returning defined values selectively, the customizer
function gives complete control over intermediate container
initialization while preserving Lodash's safe path traversal.