How Lodash updateWith Handles Missing Paths

In the Lodash JavaScript library, _.updateWith modifies a property value at a specific path of an object using an updater function, while providing a customizer callback to define how resolved path segments are generated. When encountering missing paths along the specified traversal route, _.updateWith creates the necessary intermediate objects or arrays dynamically to ensure the path exists before applying the updater function.

Default Creation Behavior

If intermediate elements along the specified path do not exist, _.updateWith will generate them automatically based on the property keys:

Once the complete path has been created, the final property value is passed into the updater function. Because the target leaf property did not previously exist, the updater receives undefined as its argument and sets the property to whatever value the updater returns.

const _ = require('lodash');

const object = {};

_.updateWith(object, 'users[0].details.age', (n) => (n ? n + 1 : 18));

console.log(object);
// Output: { users: [ { details: { age: 18 } } ] }

In this example, users did not exist, so an array was generated to satisfy [0]. Next, details did not exist, so an object was created inside the array. Finally, age evaluated to undefined in the updater callback, defaulting the value to 18.

Customizing Missing Paths with the Customizer

The primary distinction between _.update and _.updateWith is the fourth parameter: the customizer function. When a missing path is encountered, Lodash calls the customizer to determine how to create the missing intermediate segment:

customizer(nsValue, key, nested)

If the customizer returns a value, that value is used as the intermediate object or collection. If the customizer returns undefined, Lodash falls back to its default behavior of generating either an array or a plain object.

const _ = require('lodash');

const object = {};

// Force missing segments to be initialized as Map instances instead of plain objects
_.updateWith(
  object,
  'nested.key',
  () => 'final value',
  (nsValue) => (_.isObject(nsValue) ? nsValue : new Map())
);

console.log(object.nested instanceof Map);
// Output: true

Summary of Execution Flow

  1. Lodash traverses each segment in the given path.
  2. If a segment does not exist, the customizer function is invoked.
  3. If the customizer returns a value, that value becomes the container for subsequent path segments.
  4. If the customizer returns undefined (or is omitted), Lodash creates an array for numeric keys or an object for string keys.
  5. Upon reaching the end of the path, the updater is invoked with the existing leaf value (or undefined if missing) and the returned value is written to the destination.