I need to create an empty object at a deep path but only if it is not exist and return a reference to that object.
This can be acplished by using _.has
, _.set
and then _.get
let path = 'some.deep.path';
if (!_.has(object, path)) {
_.set(object, path, {});
}
let descendant = _.get(object, path);
What I would like instead, is to avoid repeating the object
, path
(or introducing the path
variable to avoid repeating the path
value)
I wonder if there's a way to do that without extra function/library.
List of lodash methods that accept or deal with a path. Alphabetically
This includes methods that deal with arrays/collections and accept the _.property
iteratee/predicate shorthand, i.e. when a string is provided as an argument, it is passed over to the _.property
method that creates a function which returns the value at path of a given object (that was given by the main function, e.g. _.map
).
Array
- v4.0.0 _.differenceBy(array, [values], [iteratee=_.identity])
- v3.0.0 _.dropRightWhile(array, [predicate=_.identity])
- v3.0.0 _.dropWhile(array, [predicate=_.identity])
- v1.1.0 _.findIndex(array, [predicate=_.identity], [fromIndex=0])
- v2.0.0 _.findLastIndex(array, [predicate=_.identity], [fromIndex=array.length-1])
- v4.0.0 _.intersectionBy([arrays], [iteratee=_.identity])
- v4.0.0 _.sortedIndexBy(array, value, [iteratee=_.identity])
- v4.0.0 _.sortedLastIndexBy(array, value, [iteratee=_.identity])
- v3.0.0 _.takeRightWhile(array, [predicate=_.identity])
- v3.0.0 _.takeWhile(array, [predicate=_.identity])
- v4.0.0 _.unionBy([arrays], [iteratee=_.identity])
- v4.0.0 _.uniqBy(array, [iteratee=_.identity])
- v4.0.0 _.xorBy([arrays], [iteratee=_.identity])
- v4.1.0 _.zipObjectDeep([props=[]], [values=[]])
Collection
- v0.5.0 _.countBy(collection, [iteratee=_.identity])
- v0.1.0 _.every(collection, [predicate=_.identity])
- v0.1.0 _.filter(collection, [predicate=_.identity])
- v0.1.0 _.find(collection, [predicate=_.identity])
- v0.1.0 _.groupBy(collection, [iteratee=_.identity])
- v4.0.0 _.invokeMap(collection, path, [args])
- v0.1.0 _.map(collection, [iteratee=_.identity])
- v3.0.0 _.partition(collection, [predicate=_.identity])
- v0.1.0 _.reject(collection, [iteratee=_.identity])
- v0.1.0 _.some(collection, [iteratee=_.identity])
Math
- v4.0.0 _.maxBy(array, [iteratee=_.identity])
- v4.7.0 _.meanBy(array, [iteratee=_.identity])
- v4.0.0 _.minBy(array, [iteratee=_.identity])
- v4.0.0 _.sumBy(array, [iteratee=_.identity])
Object
- v1.0.0 _.at(object, [paths])
- v1.1.0 _.findKey(object, [predicate=_.identity])
- v2.0.0 _.findLastKey(object, [predicate=_.identity])
- v3.7.0 _.get(object, path, [defaultValue])
- v0.1.0 _.has(object, path)
- v4.0.0 _.hasIn(object, path)
- v4.0.0 _.invoke(object, path, [args])
- v2.4.0 _.mapValues(object, [iteratee=_.identity])
- v0.1.0 _.omit(object, [paths])
- v0.1.0 _.pick(object, [paths])
- v0.1.0 _.result(object, path, [defaultValue])
- v3.7.0 _.set(object, path, value)
- v4.0.0 _.setWith(object, path, value, [customizer])
- v4.0.0 _.unset(object, path)
- v4.6.0 _.update(object, path, updater)
- v4.6.0 _.updateWith(object, path, updater, [customizer])
Util
- v4.0.0 _.iteratee([func=_.identity])
- v3.2.0 _.matchesProperty(path, srcValue)
- v3.7.0 _.method(path, [args])
- v3.7.0 _.methodOf(object, [args])
- v2.4.0 _.property(path)
- v3.0.0 _.propertyOf(object)
- v4.0.0 _.toPath(value)
Seq
Chained methods, i.e. methods that are called in a sequence, e.g.:
_(value).chained().method().value();
- v1.0.0 _.prototype.at([paths])
I need to create an empty object at a deep path but only if it is not exist and return a reference to that object.
This can be acplished by using _.has
, _.set
and then _.get
let path = 'some.deep.path';
if (!_.has(object, path)) {
_.set(object, path, {});
}
let descendant = _.get(object, path);
What I would like instead, is to avoid repeating the object
, path
(or introducing the path
variable to avoid repeating the path
value)
I wonder if there's a way to do that without extra function/library.
List of lodash methods that accept or deal with a path. Alphabetically
This includes methods that deal with arrays/collections and accept the _.property
iteratee/predicate shorthand, i.e. when a string is provided as an argument, it is passed over to the _.property
method that creates a function which returns the value at path of a given object (that was given by the main function, e.g. _.map
).
Array
- v4.0.0 _.differenceBy(array, [values], [iteratee=_.identity])
- v3.0.0 _.dropRightWhile(array, [predicate=_.identity])
- v3.0.0 _.dropWhile(array, [predicate=_.identity])
- v1.1.0 _.findIndex(array, [predicate=_.identity], [fromIndex=0])
- v2.0.0 _.findLastIndex(array, [predicate=_.identity], [fromIndex=array.length-1])
- v4.0.0 _.intersectionBy([arrays], [iteratee=_.identity])
- v4.0.0 _.sortedIndexBy(array, value, [iteratee=_.identity])
- v4.0.0 _.sortedLastIndexBy(array, value, [iteratee=_.identity])
- v3.0.0 _.takeRightWhile(array, [predicate=_.identity])
- v3.0.0 _.takeWhile(array, [predicate=_.identity])
- v4.0.0 _.unionBy([arrays], [iteratee=_.identity])
- v4.0.0 _.uniqBy(array, [iteratee=_.identity])
- v4.0.0 _.xorBy([arrays], [iteratee=_.identity])
- v4.1.0 _.zipObjectDeep([props=[]], [values=[]])
Collection
- v0.5.0 _.countBy(collection, [iteratee=_.identity])
- v0.1.0 _.every(collection, [predicate=_.identity])
- v0.1.0 _.filter(collection, [predicate=_.identity])
- v0.1.0 _.find(collection, [predicate=_.identity])
- v0.1.0 _.groupBy(collection, [iteratee=_.identity])
- v4.0.0 _.invokeMap(collection, path, [args])
- v0.1.0 _.map(collection, [iteratee=_.identity])
- v3.0.0 _.partition(collection, [predicate=_.identity])
- v0.1.0 _.reject(collection, [iteratee=_.identity])
- v0.1.0 _.some(collection, [iteratee=_.identity])
Math
- v4.0.0 _.maxBy(array, [iteratee=_.identity])
- v4.7.0 _.meanBy(array, [iteratee=_.identity])
- v4.0.0 _.minBy(array, [iteratee=_.identity])
- v4.0.0 _.sumBy(array, [iteratee=_.identity])
Object
- v1.0.0 _.at(object, [paths])
- v1.1.0 _.findKey(object, [predicate=_.identity])
- v2.0.0 _.findLastKey(object, [predicate=_.identity])
- v3.7.0 _.get(object, path, [defaultValue])
- v0.1.0 _.has(object, path)
- v4.0.0 _.hasIn(object, path)
- v4.0.0 _.invoke(object, path, [args])
- v2.4.0 _.mapValues(object, [iteratee=_.identity])
- v0.1.0 _.omit(object, [paths])
- v0.1.0 _.pick(object, [paths])
- v0.1.0 _.result(object, path, [defaultValue])
- v3.7.0 _.set(object, path, value)
- v4.0.0 _.setWith(object, path, value, [customizer])
- v4.0.0 _.unset(object, path)
- v4.6.0 _.update(object, path, updater)
- v4.6.0 _.updateWith(object, path, updater, [customizer])
Util
- v4.0.0 _.iteratee([func=_.identity])
- v3.2.0 _.matchesProperty(path, srcValue)
- v3.7.0 _.method(path, [args])
- v3.7.0 _.methodOf(object, [args])
- v2.4.0 _.property(path)
- v3.0.0 _.propertyOf(object)
- v4.0.0 _.toPath(value)
Seq
Chained methods, i.e. methods that are called in a sequence, e.g.:
_(value).chained().method().value();
- v1.0.0 _.prototype.at([paths])
5 Answers
Reset to default 4You can avoid the _.has
with this:
var path = 'some.deep.path',
descendant = _.get(object, path);
if (!descendant) {
descendant = {};
_.set(object, path, descendant);
}
Thus traversing the path only 2 times, instead of 3.
There's a _.deepDefault
method in an extra lodash-deep library that checks if the value at the propertyPath
resolves to undefined
, and sets it to defaultValue
if this is the case:
var descendant = _.deepDefault(object, 'some.deep.path', {});
That library is not updated anymore because Lodash now supports most of the functionality natively so here's an implementation as a lodash mixin function:
function getOrSetDefault(object, path, defaultValue) {
const descendant = _.get(object, path);
if (descendant === undefined) {
_.set(object, path, defaultValue);
}
return descendant;
}
_.mixin({ getOrSetDefault });
const a = _.getOrSetDefault(object, 'some.deep.path', 42);
const b = _.getOrSetDefault(object, 'some.other.deep.path', {});
b.c = 42;
With the new optional chaining operator and a logical nullish assignment there would be no need to use lodash for this particular case (if it is acceptable to populate/define some.deep.path
with a default value), e.g.:
some?.deep?.path ??= {}
const { foo } = some.deep.path
Unfortunately optional chaining assignments are not available yet. See https://stackoverflow./a/58882121/1949503
There would be just one drawback in that there'll be a need to repeat property accessors (some.deep.path
) to retrieve the value. However in this case we will have autopletion using Typescript/JSDoc unlike in lodash funcions' path argument (either string or array)
If there's only a need to get value at path the vanilla try/catch
could be sufficient:
let foo: Foo;
try {
foo = some.deep.path.foo
} catch {}
Now you can use _.setWith
setWith({}, "some.deep.path", value, Object);
returning
{some:{deep:{path:4}}
You can go straight to the point in one line with
const object = _.set({}, 'some.deep.path', {value: 'foo'})
Or, if you already have an object that you want to update
const obj = {some: {deep: {path: {barbecue: '