Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better way to Get Property Than using Lodash

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}`);
})();
like image 276
PositiveGuy Avatar asked Oct 30 '17 19:10

PositiveGuy


2 Answers

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);

A better solution might be the stage-1 proposal-optional-chaining if it makes it into the spec, although you can use it with a babel plugin right now:

const obj = {
  foo: {
    bar: {
      baz: 42,
    },
  },
};

const baz = obj?.foo?.bar?.baz; // 42

const safe = obj?.qux?.baz; // undefined
like image 158
Ori Drori Avatar answered Nov 11 '22 17:11

Ori Drori


_.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.

like image 36
Ante Gulin Avatar answered Nov 11 '22 16:11

Ante Gulin