2
\$\begingroup\$

I was writing a function that takes an argument and returns a truth value.

My initial idea was just return !!value, but this will fail for '0', {} or []. I know that '0' should be true but in my application we are considering it false.

So, my function should return false for:

  • Falsey values: false, 0, undefined, null, ''
  • Blank values: {}, []
  • Special requirement: '0'

So considering it, I updated code to following to handle

var isTrue = function(value) {
  switch (typeof(value)) {
    case "string":
      return !(value === '0' || value === 'false');
    case "number":
    case "boolean":
      return !!value;
    case "object":
      return Object.keys(value).length > 0
  }
}

console.log(false,isTrue(false))
console.log(true,isTrue(true))
console.log(0,isTrue(0))
console.log(1,isTrue(1))
console.log('0',isTrue('0'))
console.log('1',isTrue('1'))
console.log('test',isTrue('test'))
console.log('Object',isTrue({}))
console.log('Array',isTrue([]))

Now this works fine. It does not include handling for functions but that is out of scope of current question. My question is, is there a better way to handle the same?

\$\endgroup\$
  • \$\begingroup\$ My I ask what the bigger picture here is? In what context are you using this function? IMHO "falseyness" is a concept closely tied to the programming language itself and "redefining" it like this can be a pitfall for someone else using your code. I looks a bit like you are trying to get JS to behave how you think it should, instead of embracing the programming style of JS. \$\endgroup\$ – RoToRa Oct 28 '16 at 6:41
  • \$\begingroup\$ So the thing is, this is a utility function shared across apps. As a convention, we use 0 as false and 1 as true. But some apps depend on localStorage, so sometimes we get '0' or '1'. I also thought to extend its scope as it a generic function, so added functionality for boolean and boolean as string('true', 'false') and object/arrays \$\endgroup\$ – Rajesh Oct 28 '16 at 7:23
  • \$\begingroup\$ I don't think extending it like that, is a good idea. What if someone uses this seeming "generic" function in a different context, but then in the original context the requirements change? Personally I'd leave it to handle the original requirements as close as possible and give it a less generic name, such as isMySpecialPropertyTrue (with "MySpecialProperty" of course a phrase referring to the context you are using it in). \$\endgroup\$ – RoToRa Oct 28 '16 at 7:36
  • \$\begingroup\$ @RoToRa Point excepted. I'll change my function name to something less generic. Also, if there are any other pointers that you can think of, please let me know. Thanks \$\endgroup\$ – Rajesh Oct 28 '16 at 7:38
1
\$\begingroup\$

I thought it might be faster to:

  • first merely evaluate the value "standard" truth with !!value
  • then add conditions for your special requirements (including 'false', not cited in your question but present in your code)

This way, it also reduces the code, like this:

var isTrue = function(value) {
  return (!!value && value !== '0' && value !== 'false'
    && !!(typeof value !== 'object' || Object.keys(value).length));
}

console.log(false,isTrue(false));
console.log("'false'",isTrue('false'));
console.log(true,isTrue(true));
console.log(0,isTrue(0));
console.log(1,isTrue(1));
console.log("'0'",isTrue('0'));
console.log("'1'",isTrue('1'));
console.log("'test'",isTrue('test'));
console.log('{}',isTrue({}));
console.log('[]',isTrue([]));
console.log('Object',isTrue({a: 1}));
console.log('Array',isTrue([1]));

Now we can observe that it works more than 2 times faster.
Here an execution of the same cases set as above, repeated 100 times:

var isTrue = function(value) {
  switch (typeof(value)) {
    case "string":
      return !(value === '0' || value === 'false');
    case "number":
    case "boolean":
      return !!value;
    case "object":
      return Object.keys(value).length > 0
  }
}

var isTrue_2 = function(value) {
  return (!!value && value !== '0' && value !== 'false'
    && !!(typeof value !== 'object' || Object.keys(value).length));
}

var values = [false, 'false', true, 0, 1, '0', '1', 'test', {}, [], {a: 1}, [1]],
    times = 100;

console.time('isTrue');
for (var i = 0; i < times; i++) {
  for (var value of values) {
    isTrue(value);
  }
}
console.timeEnd('isTrue');

console.time('isTrue_2');
for (var i = 0; i < times; i++) {
  for (var value of values) {
    isTrue_2(value);
  }
}
console.timeEnd('isTrue_2');

Last point of interest: if you can have some statistics about how much the special requirements appear, you may change the order of the conditions to put the most frequent cases first, so yet improving performance.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Since you have special falsey cases, your checking type by type looks to me as the only option. However, I'd suggest two improvements:

First one is, if the value is boolean you don't need the !!, as it will only be either True or False. Removing the operator would -slightly- improve performance. I'd replace

case "number":
case "boolean":
    return !!value;

with

case "number":
    return !!value;
case "boolean":
    return value;

The second improvement is that the switch block should have a default statement, and your case "object" is a good candidate, so I'd replace

case "object":
    return Object.keys(value).length > 0

with

default:
  return Object.keys(value).length > 0
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ The reason why I used free fall through was to reuse !!value. This will be correct for both numbers and boolean. Yes 1 extra task is done for boolean, but I guess this is where things become gray. Also, 1 good point is I missed a default value. But I thought not returning would be similar to default: return false \$\endgroup\$ – Rajesh Oct 27 '16 at 11:31
  • \$\begingroup\$ @Rajesh not returning would be like default: return undefined;. If you want your isTrue() function to return a boolean value, you need to explicitly set the default statement. \$\endgroup\$ – LostMyGlasses Oct 27 '16 at 11:46

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.