[FAQ] - How NOT to compare multiple values using if condition

###Problem:

An exercise requires you to compare the user’s input string is either “apple”, “banana” or “guava”. Armed with your new-found knowledge of if blocks and conditional operators - &&, || and ! - you decide to write the following concise code:

if(input === "banana" || "guava" || "apple")
   console.log("PASSED!");
else
    console.log("FAILED!");

Run online
Did you instead use commas like this: if(input === "banana", "guava", "apple")? Then read this post also.

You take it for a spin and it prints "PASSED!" for inputs "banana", "guava" and "apple". But then you enter words like "fruit" or "cat" or "unicorn" and it still prints "PASSED!". After several more test runs, you come to the horrific conclusion that your program always prints "PASSED".

So, in this FAQ, we are going to have a look at why this is happening and how we can fix it.


###Why does it happen?

Remember the operator precedence table, the === operator has higher precedence than ||, so after applying parenthesis, the actual manner in which the interpreter reads our code is:

if ( (input === "banana") || ("guava") || ("apple") )

which are three separate expressions joined by the || operator:

  1. input === "banana"
  2. "guava"
  3. "apple"

Notice the last two expressions, which are not really expressions at all but simple string literals!

However, due to the way we wrote our if condition, the last two expressions are forcibly interpreted by the interpreter as a boolean value (true or false). Now, remember that any string that is empty ("") is interpreted to be false and all non-empty strings are true. So, our "guava" string is read to be true, and so is "apple", as they are not empty strings.

Now, in expressions 1, 2 and 3, expressions 2 and 3 are always true, since these are constant string literals and do not depend on user input. Expressions 1, 2 and 3 are joined by the || operator, so even if one expression is true the entire condition becomes true. Hence, our if block evaluates to true and so "PASSED!" is printed.


###How to fix this problem?

There are three ways to do this:

1. Write your condition properly

Instead of:

if(input === "banana" || "guava" || "apple")

write:

if(input === "banana" || input === "guava" || input === "apple")

2. Use switch-case with a combination of fall-through

switch(input){
    case "banana:"
    case "guava":
    case "apple":
        console.log("PASSED!"); break;
}

3. Use arrays

var valid = ["banana", "guava", "apple"];

if(valid.indexOf(input) > -1) console.log("PASSED!")

Note: You might not have been taught arrays or switch-case up until this point in the course. Do not worry, these are only indicative and alternative solutions and you can always use the first solution.


Hope it helps! :smiley:

7 Likes

Another related (and wrong) if condition is:

if (input === "banana", "guava", "apple")

which is using the comma operator (,). Remember that the comma operator evaluates all its operands from left to right, but only returns the value of the right most operand.

Here, the three operands are: input === "banana", "guava" and "apple". All the the three are evaluated, but that doesn’t really matter because only the right-most value ("apple") is returned, which effectively makes our if condition look like:

if("apple")

again, remember that:

Hence, the if condition always remains true.

4 Likes

I’ve also seen one, who compared three values like you would do in in a sentence,
which means in clean code - according to this example:

if (input === "banana", "guava" || "apple")

*facepalm*

3 Likes

Another slightly related condition, that is wrong:

if(start <= amount <= end)

NOTE: This condition is actually perfectly valid in Python. If you are using Python, you need not read this post.

This condition is wrong as well. Why, you ask?

Both the operators cannot be evaluated simultaneously. Hence, the first <= is taken, which leads to the start <= variable part of the condition being evaluated first.

Remember that <=, >=, <, >, == and != are all relational operators and they return a boolean value. So, start <= variable on evaluation returns a boolean value of true/false.

Now, our condition will become true/false <= end which does not make any sense, as we are comparing a boolean value with a number (end). So, our interpreter will convert true to 1 and false to 0. And thus our condition will look like: 0/1 <= end, which is NOT quite the result we expect.

Special note for Java: Java does not support converting a boolean implicitly to an integer value. So, as soon as it sees start <= amount <= end, it will raise a compile time error.

3 Likes