0
\$\begingroup\$

I try to get location data by a external HTTP call. For this API, any part in the data can be missing. Initially I wrote the following code to check whether data.res.accounts[0].contacts[0].location exists:

const getLocation = async () => {
  const data = await fetchLocation();
  if (
    data == null
    data.res == null ||
    data.res.accounts == null ||
    data.res.accounts[0] == null ||
    data.res.accounts[0].contacts == null ||
    data.res.accounts[0].contacts[0] == null ||
    data.res.accounts[0].contacts[0].location == null
  ) {
    return null;
  }
  return data.res.accounts[0].contacts[0].location;
}

I have tried to simplify the mixed object and array code. One way of doing this is using JavaScript's new conditional operator ?..

For example if they are all objects then I can use something like:

if (data?.res?.account?.contact?.location == null) {
  return null;
}

However my data is mixed with arrays and objects. This is the best I've come up with:

if (
  data?.res?.accounts == null ||
  data.res.accounts[0]?.contacts == null ||
  data.res.accounts[0].contacts[0]?.location == null
) {
  return null;
}

Can I simplify it further?

\$\endgroup\$
  • 3
    \$\begingroup\$ This question lacks any indication of what the code is intended to achieve. To help reviewers give you better answers, please add sufficient context to your question, including a title that summarises the purpose of the code. We want to know why much more than how. The more you tell us about what your code is for, the easier it will be for reviewers to help you. The title needs an edit to simply state the task. \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Sep 14 at 21:24
  • \$\begingroup\$ @SᴀᴍOnᴇᴌᴀ thanks for the guide, just added more details. \$\endgroup\$ – Hongbo Miao Sep 14 at 21:31
  • 1
    \$\begingroup\$ Hey the code you have posted looks like it's missing context to be reasonably reviewed. As it looks like a generic best practice question. Could you provide more code as this looks like an example, and the answer to best practices is "it depends" \$\endgroup\$ – Peilonrayz Sep 14 at 21:33
  • \$\begingroup\$ @Peilonrayz thanks, added more details again. \$\endgroup\$ – Hongbo Miao Sep 14 at 21:42
  • 1
    \$\begingroup\$ Hey @HongboMiao I think you've added more text in response to my comment. I have edited your text, but I don't think the problem lies there. My problem with your post is that there is a lack of surrounding code, that can give us insight on if this is a good idea. Currently for me to write an answer I'd need to write a short novel on how ?. can be used in any situation, because I can't see what your situation is. \$\endgroup\$ – Peilonrayz Sep 14 at 22:14
6
\$\begingroup\$

Optional chaining can be used not only with object property lookup with dot notation, but also with bracket notation, by putting ?. before the [:

const data = { res: {}};
console.log(data?.res?.accounts?.[0]?.contacts?.[0]?.location == null)

Just to be thorough, you can also use optional chaining with function calls:

const data = { res: { }};
console.log(data?.res?.accounts?.[0]?.contacts?.[0].location?.includes?.('foobar'));

const data2 = { res: { accounts: [{ contacts: [{ location: 'foobarbaz' }] }] } };
console.log(data2?.res?.accounts?.[0]?.contacts?.[0].location?.includes?.('foobar'));

const data3 = { res: { accounts: [{ contacts: [{ location: 7 }] }] } };
console.log(data3?.res?.accounts?.[0]?.contacts?.[0].location?.includes?.('foobar'));

That said, having so many nested properties whose existence is uncertain is a bit of a code smell, because any time you want to look something up, you'll have to check the existence of all intermediate properties first. Despite the fact that you can do it concisely with optional chaining nowadays, it's quite a strange data structure. If you have control over fetchLocation, consider if you can refactor it so that fewer intermediate properties may be missing. (For example, if you could change it so that every account has a contacts array, and the array might be empty (but not null nor undefined), that'd be a step in the right direction).

Also, keep in mind that optional chaining is extremely new syntax. If this is going to be running on a public website, make sure to transpile your code down to ES6 or ES5 for production so that those with older browsers can consume your code.

On another note, you might consider using === instead of ==. ESLint rule: eqeqeq. == has some pretty odd behaviors involving type coercion that developers shouldn't be forced to look up in order to be 100% confident of what logic a particular section of code is implementing. For example, you could use ?? instead, to handle cases of undefined or null:

const location = data?.res?.accounts?.[0]?.contacts?.[0]?.location;
return location ?? null;

(Or, if location will always be a non-empty string, you could return location || null, which might be a bit better)

Also, the conditional operator is not in any of the code in your question or this answer. The conditional operator uses syntax of the form:

const resultingExpression = condition ? expression1 : expression2;
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Really appreciate, both accounts?.[0]? and nullish coalescing operator ?? are new to me! \$\endgroup\$ – Hongbo Miao Sep 15 at 15:07
3
\$\begingroup\$

If you use the Nullish Coalescing Operator you can do something similar to the following:

const getLocation = async () => {
  const nestMap = ['res', 'accounts', 0, 'contacts', 0, 'location'];
  const data = await fetchLocation();

  const searchNest = (data, property) => (data ?? false) ?
  data[property] : null;

  return nestMap.reduce(searchNest, data)
}

This basically takes a predetermined list of properties (nestMap) and proceeds to check an input object (data) for any nullish value down the list of properties. If it reaches the end of the nest without encountering null it returns the last property in the nestmap (location in this case) otherwise it returns null.

Note: If the returned location is undefined you will get null just like your own code returns.

Not sure if this is what you are looking for but it might provide some insights into simplifying either way.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.