Thursday, September 10, 2015

Avoid multiple NOTs in conditions

TL;DR
Write code like how you are speaking in a conversation.


What's the flaw in this code?

if (e.which === 8 && e.target.nodeName !== "INPUT" || e.target.nodeName !== ")
    e.preventDefault();
}


How to express condition without being confused on when to use || or && operator?


First off, let's correct the most fundamental error on the logic above. && operator has higher precedence than || operator, or we can say && operator higher stickiness than || operator. The above is interpreted as:

if 
(

    (e.which === 8 && e.target.nodeName !== "INPUT")
 
    || 

    e.target.nodeName !== "SELECT"

)
{ 
    e.preventDefault();
}


To correct the incorrect unparenthesized code, use parenthesis:

if ( e.which === 8 && (e.target.nodeName !== "INPUT" || e.target.nodeName !== "SELECT") ) { 
    e.preventDefault();
}


Or since the code is not too indented, just nest the condition:

if (e.which === 8) {

    if (e.target.nodeName !== "INPUT" || e.target.nodeName !== "SELECT") { 
         e.preventDefault();
    }

}


Now that we get the harm out of the way, let's fix the second problem:

if (e.target.nodeName !== "INPUT" || e.target.nodeName !== "SELECT") {

}


What's wrong with the code above?

Yes, it always evaluates to true regardless of the value of e.target.nodeName.

This is where most developers are having a hard time when formulating a condition. The best way to express a logic is to express it like how you will say it in actual conversation. For example, if you want to advise your kid that if someone visits and he is not John, Paul, George, Ringo, then shoo him way. You will not say in English, "if he is not John, not Paul, not George, not Ringo, then shoo him away."

You will not repeat the NOT when you actually talk, instead you'll say: "if he is not John, Paul, George, Ringo, then shoo them away." You will only say NOT once. However, if you feel multiple NOTs is readable, you have to change the OR to AND to make the logic correct:

if e.target.nodeName ≠ "INPUT" and e.target.nodeName ≠ "SELECT" then
    disable navigation

But really, you won't say that in real life, you'll only say NOT once.

To code a correct logic, think of how you will write the condition if you will refactor or make a code shortcut. If you will move the condition to a function, you'll write it like this:

if not inputEditable(e.target.nodeName) then
    disable navigation


Now with that refactored code, the code writes itself:

inputEditable(nodeName) 

    if nodeName = "INPUT" or nodeName = "SELECT" then
        return true
    else
        return false

Here's the version when the condition above is inlined in if:

if not ( e.targetNodeName = "INPUT" or e.target.nodeName = "SELECT" ) then
    disable navigation


See? No more multiple NOTs, and the best thing is, the code is correct.


Just write things with one NOT, the right logic will write itself.



Happy Coding!

No comments:

Post a Comment