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
|
4 Answers
Reset to default 10Lodash'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!
const { profileId } = account || {};
is equivilent to thisconst profId = (account || {}).profileId;
– O'Mutt Commented Jun 3, 2023 at 22:11