最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Create an empty object at deep path with lodash - Stack Overflow

programmeradmin4浏览0评论

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])
Share Improve this question edited Jun 10, 2017 at 23:32 Steven Pribilinskiy asked Sep 4, 2015 at 11:48 Steven PribilinskiySteven Pribilinskiy 2,0021 gold badge21 silver badges21 bronze badges
Add a ment  | 

5 Answers 5

Reset to default 4

You 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: '
发布评论

评论列表(0)

  1. 暂无评论