-1

What is the correct way to test this condition when 'members' and 'departments' may not exist? Is there a way to check for their existence while testing the value without causing an error?

if (state.staff.members.length < 5 || state.staff.departments.length < 5) {}
Zakaria Acharki
  • 66,747
  • 15
  • 75
  • 101
  • If state.staff.members or state.staff.departments does not exist then there are <5 staff or members and so i would like the function to pass –  Jul 13 '18 at 15:55
  • 1
    I don't understand your comment. Can you clarify your condition and what your expected outcomes are in as much detail as possible? – zero298 Jul 13 '18 at 15:59
  • Clearer question at https://stackoverflow.com/questions/51329019/test-values-of-multiple-keys-which-may-not-exist –  Jul 13 '18 at 16:04

5 Answers5

2

The cleanest (and in my opinion, best) way to do this is with a try...catch block. That way, you can safely and gracefully fail if the properties you are looking for don't exist, like so:

var state;

function myFunction() {
  try {
    if (state.staff.members.length < 5 || state.staff.departments.length < 5) {
      return "PASS";
    }
  } catch (e) {
    console.log(e.toString());
    return "PASS"
  }
  return "FAIL";
}

console.log(myFunction());
mhodges
  • 10,938
  • 2
  • 28
  • 46
  • If state.staff.members or state.staff.departments does not exist then there are <5 staff or members and so i would like the function to pass –  Jul 13 '18 at 15:57
  • @user759885 Updated. Is that what you're looking for? – mhodges Jul 13 '18 at 16:00
1

Lots and lots of falsy checks

if (
  (
    state &&
    state.staff &&
    state.staff.members &&
    state.staff.members.length < 5
  ) ||
  (
    state &&
    state.staff &&
    state.staff.departments &&
    state.staff.departments.length < 5
  )
) {
   console.log("foo");
}

I would suggest taking the lodash approach with _.get() instead to encapsulate the checks.

if (
    _.get(state, "staff.members.length") < 5 || 
    _.get(state, "staff.departments.length") < 5
){
  console.log("foo");
}
zero298
  • 25,467
  • 10
  • 75
  • 100
  • `try...catch` is built right in to JS - there's no need to litter your code with stuff like this – mhodges Jul 13 '18 at 15:54
  • @mhodges True, but `try/catch` seems slower [Try/Catch Error Perf](https://jsperf.com/try-catch-error-perf/3) – zero298 Jul 13 '18 at 15:56
  • 3% slower with 1 expression check in your `if` statement. I have a feeling that evaluating 8 expressions will be slower than letting try/catch do its thing. – mhodges Jul 13 '18 at 16:03
  • @mhodges I'm not getting 3%, I'm getting 100% slower using `try/catch`. I'm able to do **832,007,277** if checks vs **244,517** try/catch in Chrome. I can do **1,574,399,733** if checks in Firefox. – zero298 Jul 13 '18 at 16:10
  • On the perf from 2012 that you posted lol. Go to the most recent one – mhodges Jul 13 '18 at 16:11
  • 1
    @mhodges Ah, my bad. I wasn't using the most recent revision. – zero298 Jul 13 '18 at 16:13
  • That being said, I have no idea why someone randomly downvoted all of the answers on this question..? – mhodges Jul 13 '18 at 16:22
0
if ((state.staff.members && state.staff.members.length < 5) || (state.staff.departments && state.staff.departments.length < 5)) {}

This assumes that you only want the condition to pass when at least one of the properties is set.

Edit: In response to your comment I would then write it the following way

if ((state.staff.members && state.staff.members.length < 5) 
    || (state.staff.departments && state.staff.departments.length < 5)
    || state.staff.hasOwnProperty('members') === false
    || state.staff.hasOwnProperty('departments') === false
) {}
Jon Wyatt
  • 79
  • 6
  • Sorry my question was not clear. If state.staff.members or state.staff.departments does not exist then there are <5 staff or members and so i would like the function to pass –  Jul 13 '18 at 15:55
0

Try this :)

if (
  (state.staff.members && state.staff.members.length < 5) || 
  (state.staff.departments && state.staff.departments.length < 5)
) {

   // do something

}
UtkarshPramodGupta
  • 7,486
  • 7
  • 30
  • 54
  • If state.staff.members or state.staff.departments does not exist then there are <5 staff or members and so i would like the function to pass –  Jul 13 '18 at 15:57
  • Your function would actually pass. "&&" operator doesn't evaluates the right operand at all when the left operand is a falsy value i.e. false, undefined, null or the likes. – UtkarshPramodGupta Jul 13 '18 at 16:00
0

For those scenarios, I like to use a little helper like this:

/**
 * Accesses nested properties and returns a default value if it encounters null or undefined along the way.
 * @param fallback A fallback value if any property or its value is null or undefined.
 * @param path The path to access a nested property
 */
export const propOrDefault = <T, R>(fallback: R) => (...path: string[]) => (obj: T | null | undefined): R =>
  path.reduce((partialObj, propertyName) => {
    if (partialObj && propertyName && partialObj.hasOwnProperty(propertyName)) {
      return partialObj[propertyName] || fallback;
    }

    return fallback;
  }, obj);

This is TypeScript, but it should get the point across. And the additional type info might help with understanding.

Example usage:

const isValid = propOrDefault(0)('staff', 'members', 'length')(state) < 5;

As you can see, this helper uses currying so you could pre-configure it for later use like so:

const propOrTen = propOrDefault(0);
const countStaffMembers = propOrTen('staff', 'members', 'length');
const result = countStaffMembers(state);

There are loads of libraries out there with similar tools like lodash and ramda.

Xceno
  • 903
  • 9
  • 22