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

javascript - Better way to Get Property Than using Lodash - Stack Overflow

programmeradmin4浏览0评论

So we have this crap all over in our code, IMO too much lodash. For example:

const profileId = _.get(account, 'profileId', null);
const player = _.get(someCustomObject, 'player'); // Why??????  why not just someCustomObject.player?

Is it me or is it ridiculous to be using lodash for everything when you can just access object properties by the object! In general we're using lodash and it's overkill in many places and makes the code way more verbose and hard to read.

In this case, we don't really need lodash either:

const profileId = _.get(account, 'profileId', null);

What would be a way to do that without lodash? That's my question. Here are a few thoughts:

const profileId = (account && account.profileId) || null

Any other ideas?

UPDATE

Interesting, went with Ori's answer but just an observation here. I wanted to remove defaulting the profileId to null just because I feel it's unnecessary. But why didn't this set profileId to the account object?

const account = {};
const game = { player: 'Sam' }

console.log(`account: ${account}`);

const { profileId } = account || {};
const { player } = game || {};

console.log(`profileId: ${profileId} // why is this undefined if account has value {}???`);
console.log(`player: ${player}`);

So we have this crap all over in our code, IMO too much lodash. For example:

const profileId = _.get(account, 'profileId', null);
const player = _.get(someCustomObject, 'player'); // Why??????  why not just someCustomObject.player?

Is it me or is it ridiculous to be using lodash for everything when you can just access object properties by the object! In general we're using lodash and it's overkill in many places and makes the code way more verbose and hard to read.

In this case, we don't really need lodash either:

const profileId = _.get(account, 'profileId', null);

What would be a way to do that without lodash? That's my question. Here are a few thoughts:

const profileId = (account && account.profileId) || null

Any other ideas?

UPDATE

Interesting, went with Ori's answer but just an observation here. I wanted to remove defaulting the profileId to null just because I feel it's unnecessary. But why didn't this set profileId to the account object?

const account = {};
const game = { player: 'Sam' }

console.log(`account: ${account}`);

const { profileId } = account || {};
const { player } = game || {};

console.log(`profileId: ${profileId} // why is this undefined if account has value {}???`);
console.log(`player: ${player}`);

Even though account is set to a literal, I still get undefined for profileId above. That's weird..?

FINAL SOLUTION (run this in codepen.io since you'll need lodash loaded)

console.log(" Example Clean Code without Lodash")
console.log('===============================')

let account = {};
const game = { player: 'Sam' }

console.log(`account: ${account}`);

const { profileId = null } = account;
const { player } = game || {};

console.log(`profileId: ${profileId}`);
console.log(`player: ${player}`);

/* using IIFE so profileId doesn't conflict with example above */
(() => {
  console.log("Example using unnecessary Lodash")
  console.log('===============================')

  let profileId = _.get(account, 'profileId', null);
  const game = { player: 'Sam' } 

  console.log(`account2: ${account}`);

  const { player } = game || {};

  console.log(`profileId: ${profileId}`);
  console.log(`player: ${player}`);
})();
Share Improve this question edited Jan 10, 2022 at 20:07 Dave Mackey 4,43221 gold badges83 silver badges144 bronze badges asked Oct 30, 2017 at 19:55 PositiveGuyPositiveGuy 20.2k30 gold badges93 silver badges151 bronze badges 2
  • "Better" is a broad term. – priyabagus Commented Oct 13, 2019 at 23:50
  • > Even though account is set to a literal, I still get undefined for profileId above. That's weird..? I would expect that since const { profileId } = account || {}; is equivilent to this const profId = (account || {}).profileId; – O'Mutt Commented Jun 3, 2023 at 22:11
Add a comment  | 

4 Answers 4

Reset to default 10

Lodash's _.get() is really good if you want to get something nested inside an object profile.player.id for example, and it also has a default if nothing is found or there's an error, like this example _.get(account, 'profileId', null);.

If it's a direct property of the object, you can use destructuring with a default:

const account = {};
const game = { player: 'Sam' }


const { profileId = null } = account || {};
const { player } = game || {};

console.log(profileId);
console.log(player);

In vanilla JS you can use optional chaining (?.):

const obj = {
  foo: {
    bar: {
      baz: 42,
    },
  },
};
 
const baz = obj?.foo?.bar?.baz; // 42
 
const safe = obj?.qux?.baz; // undefined

Here is a nice one-liner from the article "Safely Accessing Deeply Nested Values In JavaScript"

export function _get(object, path, defval = null) {
    if (typeof path === "string") path = path.split(".");
    return path.reduce((xs, x) => (xs && xs[x] ? xs[x] : defval), object);
}

You may also use this awesome library of zero dependency utilities https://github.com/angus-c/just

_.get is a helper method that is used for several reasons. It helps you reduce code/logic duplication, it's harder to make an error and easier to read and maintain your code.

If you are worried about the performance difference between _.get and (account && account.profileId) || null, I don't think you should be worried unless you have a huge codebase that calls _.get hundreads of times. Here is the lodash's get function definition and it's dependancies.

function get(object, path, defaultValue) {
  var result = object == null ? undefined : baseGet(object, path);
  return result === undefined ? defaultValue : result;
}

function baseGet(object, path) {
  path = castPath(path, object);

  var index = 0,
      length = path.length;

  while (object != null && index < length) {
    object = object[toKey(path[index++])];
  }
  return (index && index == length) ? object : undefined;
}

function castPath(value, object) {
  if (isArray(value)) {
    return value;
  }
  return isKey(value, object) ? [value] : stringToPath(toString(value));
}

function toKey(value) {
  if (typeof value == 'string' || isSymbol(value)) {
    return value;
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}

You can always perform a benchmark test for your code with 2 different variations and then decide if it makes enough difference for you to use the native implementation.

I just did a benchmark test where lodash's ._get was 95% slower than the native implementation. I guess you can afford that unless your code heavily relies on ._get as stated above. Maybe even better alternative would be to write your own helper function to get rid of the lodash's overhead and achieve the same pros of lodash's get and also near-native performance. You can define your own helper function knowing in advance what kind of data will be passed to it, thus writing a zero-overhead helper for your specific usecase.

This come out from a recent discussion:

var template = "string with {obj.prop} first of 0: {results[0].first}";
var data = {obj: {prop: 'mine'}, results:[{first:'f'},{first:'ff'}] }
var regex = /{(.*?)}/g;
var str = template.replace(regex, (e, key) => _.get(data, key, ''));
console.log(str);

but you can write a function:

const interpolate = (template, data) => template.replace(/{(.*?)}/g, (e, key) => _.get(data, key, ''));

of cource it is simpler to use:

let str = interpolate("string with {obj.prop} first of 0: {results[0].first}", data)

in general in everywhere you do not know what you are going to replace, _.get() is something good to have.

UPDATE: fix the missing regexp!

发布评论

评论列表(0)

  1. 暂无评论