4

I wrote the following Javascript function that is meant to return true when exactly one of the arguments is truthy:

function onlyOne( a, b, c) {
    return (a && !b && !c) ||
        (b && !a && !c) ||
        (c && !a && !b);
}

It works as expected, except in a couple of cases where it returns 0:

EXPECT true:
true = onlyOne( 1, 0, 0)
true = onlyOne( 0, 1, 0)
true = onlyOne( 0, 0, 1)

EXPECT false:
0 = onlyOne( 0, 0, 0)
false = onlyOne( 0, 1, 1)
false = onlyOne( 1, 0, 1)
0 = onlyOne( 1, 1, 0)
false = onlyOne( 1, 1, 1)

WHY does it return 0 rather than false in the two cases above? I see that those are the only two negative cases where c is 0, but I don't see how that makes a difference. Obviously there's something about Javascript logical operators and/or type conversion that I'm not understanding here.

Here is the full code that produces the above output. I'm running this from the Windows command line using node.js 4.3.0, if that makes any difference:

    function testOnlyOne( a, b, c) {
        result = onlyOne(a, b, c);
        console.log("%j = onlyOne( %j, %j, %j)", result, a, b, c);
    }
    
    function onlyOne( a, b, c) {
        return (a && !b && !c) ||
            (b && !a && !c) ||
            (c && !a && !b);
    }
    
    console.log("\nEXPECT true:");
    testOnlyOne( 1, 0, 0);
    testOnlyOne( 0, 1, 0);
    testOnlyOne( 0, 0, 1);
    
    console.log("\nEXPECT false:");
    testOnlyOne( 0, 0, 0);
    testOnlyOne( 0, 1, 1);
    testOnlyOne( 1, 0, 1);
    testOnlyOne( 1, 1, 0);
    testOnlyOne( 1, 1, 1);
Taplar
  • 24,788
  • 4
  • 22
  • 35
Chad
  • 1,750
  • 2
  • 16
  • 32
  • 6
    The JavaScript `&&` and `||` operators do not necessarily return a boolean value. – Pointy Jul 20 '18 at 20:37
  • 2
    If the first argument is a 0, which is falsy, there is no need to perform the following `&&` which would possibly turn it into a false, so it returns the 0. The same follow for if the first argument were a 1 for an ||, it would not need to do the following checks. This is what is referred to as short circuit logic. – Taplar Jul 20 '18 at 20:37
  • 3
    A common way to coerce results to boolean is the double bang (`!!`), as in `return !! ( (a && !b && !c) || (b && !a && !c) || (c && !a && !b) )`, which will always yield a boolean type – Will Jul 20 '18 at 20:42
  • http://jsfiddle.net/jmx543ae/ an alternative approach as a comment since, true, this question isn't about refactoring – Taplar Jul 20 '18 at 20:51

2 Answers2

1

Because if the way JavaScript checks falsy and truthy values. Values such as false, 0, '' undefined, null, etc..., are considered falsy (return value of Boolean() for any of these returns false) while others such as true, 1, 'hi', [] are truthy (return value of Boolean() for any of these returns true).

Therefore, when executing an instruction such as 0 && 'a' will always return 0 because 0 is falsy, and && operator doesn't advance after any value is false, because for && to return true all the values for which it is applied have to be truthy. On the other hand, executing an instruction such as true && 'hello' && null will always return null because all the first values up to null were truthy, so the actual statement resolves to null.

Amit Beckenstein
  • 1,220
  • 12
  • 20
0

This blog post might be worth reading to understand why js handles thing this way.

The value produced by a && or || operator is not necessarily of type Boolean. The value produced will always be the value of one of the two operand expressions.

Both && and || result in the value of (exactly) one of their operands:

  • A && B returns the value A if A can be coerced into false; otherwise, it returns B.
  • A || B returns the value A if A can be coerced into true; otherwise, it returns B.

If you want the result as a boolean, you can use "not-not", like this: !!expression

Community
  • 1
  • 1
geekley
  • 1,231
  • 10
  • 27
  • Thanks! The blog post was excellent. I was assuming && and || worked like they do in C or Java... – Chad Jul 20 '18 at 20:50
  • This behavior is more similar to python, if anything. IMO, js has very little in common with Java or C. It might be also worth checking how different types work in expressions like `if(x)` `if(!x)` `if(x==null)` – geekley Jul 20 '18 at 20:56