DEV Community

Cover image for Avoid use IF on our JS scripts
Damian Cipolat
Damian Cipolat

Posted on • Edited on

Avoid use IF on our JS scripts

Well, I write this article because lately I was dealing with source code in js that had an excessive amount of if statement,
at levels that I had never seen. That is why I think it is very important to share these simple techniques that will help us to write code without having to think about the "if" when deciding.

I am going to explain 6 ways on how to do this. The idea of ​​this is not to enter into paranoia of never using IF, it is to open the head to new ways of thinking about our decisions in JS.

Categories:

1) Ternary operator:

We are talking about this "condition ? expr1 : expr2", very easy.

Example 1:

  • Code with IF:
function saveCustomer(customer) {
  if (isCustomerValid(customer)) {
    database.save(customer)
  } else {
    alert('customer is invalid')
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Refactored code:
function saveCustomer(customer) {
  return isCustomerValid(customer)
    ? database.save(customer)
    : alert('customer is invalid')
}
Enter fullscreen mode Exit fullscreen mode
  • ES6 style:
const saveCustomer = customer =>isCustomerValid(customer)?database.save(customer):alert('customer is invalid')
Enter fullscreen mode Exit fullscreen mode

Example 2:

  • Code with IF:
function customerValidation(customer) {
  if (!customer.email) {
    return error('email is require')
  } else if (!customer.login) {
    return error('login is required')
  } else if (!customer.name) {
    return error('name is required')
  } else {
    return customer
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Refactored code:
// ES6 style custom formatted ternary magic
const customerValidation = customer =>
  !customer.email   ? error('email is required')
  : !customer.login ? error('login is required')
  : !customer.name  ? error('name is required')
                    : customer
Enter fullscreen mode Exit fullscreen mode

Example 3:

  • Code with IF:
function getEventTarget(evt) {
    if (!evt) {
        evt = window.event;
    }
    if (!evt) {
        return;
    }
    const target;
    if (evt.target) {
        target = evt.target;
    } else {
        target = evt.srcElement;
    }
    return target;
}
Enter fullscreen mode Exit fullscreen mode
  • Refactored code:
function getEventTarget(evt) {
  evt = evt || window.event;
  return evt && (evt.target || evt.srcElement);
}
Enter fullscreen mode Exit fullscreen mode

⮬ back to top

2) Short circuit:

It is a technique that uses the AND and OR operators to evaluate expressions.

https://codeburst.io/javascript-short-circuit-conditionals-bbc13ac3e9eb

true || true;
// true
true || false;
// true
false || false;
// false
Enter fullscreen mode Exit fullscreen mode

Example 1:

  • Code with IF:
const isOnline = true;
const makeReservation= ()=>{};
const user = {
    name:'Damian',
    age:32,
    dni:33295000
};

if (isOnline){
    makeReservation(user);
}

Enter fullscreen mode Exit fullscreen mode
  • Refactored code:
const isOnline = true;
const makeReservation= ()=>{};
const user = {
    name:'Damian',
    age:32,
    dni:33295000
};

//Apply the short circuit to avoid the if.
isOnline&&makeReservation(user);
Enter fullscreen mode Exit fullscreen mode

Example 2:

  • Code with IF:
const active = true;
const loan = {
    uuid:123456,
    ammount:10,
    requestedBy:'rick'
};

const sendMoney = ()=>{};

if (active&&loan){
    sendMoney();
}

Enter fullscreen mode Exit fullscreen mode
  • Refactored code:

const active = true;
const loan = {
    uuid:123456,
    ammount:10,
    requestedBy:'rick'
};

const sendMoney = ()=>{};

//Apply short circuit in this case, the loan is evaluated true because !=undefined
active && loan && sendMoney();
Enter fullscreen mode Exit fullscreen mode

⮬ back to top

3) Function delegation:

This technique mix the short circuit and separation code block with functions.

Example 1:

  • Code with IF:
function itemDropped(item, location) {
    if (!item) {
        return false;
    } else if (outOfBounds(location) {
        var error = outOfBounds;
        server.notify(item, error);
        items.resetAll();
        return false;
    } else {
        animateCanvas();
        server.notify(item, location);
        return true;
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Refactored code:
function itemDropped(item, location) {
    const dropOut = function() {
        server.notify(item, outOfBounds);
        items.resetAll();
        return false;
    }

    const dropIn = function() {
        server.notify(item, location);
        animateCanvas();
        return true;
    }

    return !!item && (outOfBounds(location) ? dropOut() : dropIn());
}
Enter fullscreen mode Exit fullscreen mode

⮬ back to top

4) Non branching strategy:

This technique try to avoid the use of switch statemente. The idea is to create a map with keys/values and using a function
to access the value of the key passed as parameter.

The idea came from this link: https://medium.com/chrisburgin/rewriting-javascript-replacing-the-switch-statement-cfff707cf045

Example 1:

  • Code with SWITCH:
switch(breed){
    case 'border':
        return 'Border Collies are good boys and girls.';
        break;  
    case 'pitbull':
        return 'Pit Bulls are good boys and girls.';
        break;  
    case 'german':
        return 'German Shepherds are good boys and girls.';
        break;
    default:
        return 'Im default'

}
Enter fullscreen mode Exit fullscreen mode
  • Refactored code:

const dogSwitch = (breed) =>({
  "border": "Border Collies are good boys and girls.",
  "pitbull": "Pit Bulls are good boys and girls.",
  "german": "German Shepherds are good boys and girls.",  
})[breed]||'Im the default';


dogSwitch("border xxx")

Enter fullscreen mode Exit fullscreen mode

⮬ back to top

5) Functions as Data:

We know that in JS the function are first class, so using it we can split the code into a function objects.

Example 1:

  • Code with IF:

const calc = {
    run: function(op, n1, n2) {
        const result;
        if (op == "add") {
            result = n1 + n2;
        } else if (op == "sub" ) {
            result = n1 - n2;
        } else if (op == "mult" ) {
            result = n1 * n2;
        } else if (op == "div" ) {
            result = n1 / n2;
        }
        return result;
    }
}

calc.run("sub", 5, 3); //2

Enter fullscreen mode Exit fullscreen mode
  • Refactored code:
const calc = {
    add : function(a,b) {
        return a + b;
    },
    sub : function(a,b) {
        return a - b;
    },
    mult : function(a,b) {
        return a * b;
    },
    div : function(a,b) {
        return a / b;
    },
    run: function(fn, a, b) {
        return fn && fn(a,b);
    }
}

calc.run(calc.mult, 7, 4); //28
Enter fullscreen mode Exit fullscreen mode

⮬ back to top

5) Polymorphism:

Polymorphism is the ability of an object to take on many forms. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object.

Example 1:

  • Code with IF:
const bob = {
  name:'Bob',
  salary:1000,
  job_type:'DEVELOPER'
};

const mary = {
  name:'Mary',
  salary:1000,
  job_type:'QA'
};

const calc = (person) =>{

    if (people.job_type==='DEVELOPER')
        return person.salary+9000*0.10;

    if (people.job_type==='QA')
        return person.salary+1000*0.60;
}

console.log('Salary',calc(bob));
console.log('Salary',calc(mary));

Enter fullscreen mode Exit fullscreen mode
  • Refactored code:
//Create function to different behaviour, same parameter call.
const qaSalary  = (base) => base+9000*0.10;
const devSalary = (base) => base+1000*0.60;

//Add function to the object.
const bob = {
  name:'Bob',
  salary:1000,
  job_type:'DEVELOPER',
  calc: devSalary
};

const mary = {
  name:'Mary',
  salary:1000,
  job_type:'QA',
  calc: qaSalary
};

//Same call.
console.log('Salary',bob.calc(bob.salary));
console.log('Salary',mary.calc(mary.salary));

Enter fullscreen mode Exit fullscreen mode

⮬ back to top

Readings:

A list of interesting links about this same topic.

Visit my github for more projects!!
https://github.com/damiancipolat?tab=repositories

Top comments (47)

Collapse
 
tomerl101 profile image
Tomer

Nice article , but i always prefer readability over number of lines :)

Collapse
 
victoor profile image
Comment marked as low quality/non-constructive by the community. View Code of Conduct
Víctor Falcón

True, and some example on this article are horrible, in my opinion.

That second ternary almost explode my brain.

Collapse
 
damxipo profile image
Damian Cipolat

well bad luck, thanks for your opinion. I consider more horrible a code like this:

export const removeProps = (source: Object, props: Array, keepReceivedProps: boolean = true) => {
return Object.keys(source).forEach(key => {
if (keepReceivedProps) {
if (!props.find(item => item == key)) delete source[key];
} else {
if (props.find(item => item == key)) delete source[key];
}
});
};

This code is from a project make maybe from JAVA developers that come to JS or for JS Jr developer. the if nesting I think is very bad. So for this reason I'm looking for alternatives to avoid write this type of code and publish material to try the developers in find new alternatives

Collapse
 
lucamuscat profile image
Luca Muscat

Slight correction, it's no longer a ternary if it contains more than 3 values on the right hand side (it becomes an n-ary operator ;) )

Thread Thread
 
damxipo profile image
Damian Cipolat

NICE thanks for the data

Collapse
 
carlosbbt profile image
Carlos Moran

And the lack of spacing, my eslint config would choke on the examples.

Thread Thread
 
damxipo profile image
Damian Cipolat

They were not developed using eslint, you can execute them directly from node in a new project

Collapse
 
blindfish3 profile image
Ben Calder

Some useful suggestions here; but some should come with health warnings:

  • even a single nested ternary can be hard to read. Your example with multiple nesting should never get through code review. Nesting ternaries should be discouraged
  • short circuit can be useful; but can often result in mistakes: e.g. returning false when another type is expected (this is a good use for ternary). Linting rules often disallow conditionIsTrue && functionCall() outside an assignment expression.
  • the itemDropped example would work better using guard expressions: you still use if but can get rid of the redundant use of else. Simpler and more readable than your suggestion.
Collapse
 
damxipo profile image
Damian Cipolat

I am glad to see that it generates reactions in other devs, it is the idea of ​​the article. As I said above, the idea is not to stop using if it's just something to shake them and they can think about decisions in another way.

Collapse
 
blindfish3 profile image
Ben Calder

Generating reactions is all well and good, but I think it would be more productive to put forward examples of alternatives to if in contexts where they are appropriate/useful. Given the introduction describing code with too many ifs; some junior devs may mistakenly believe all your solutions represent good practice...

Thread Thread
 
damxipo profile image
Damian Cipolat

In the header it says that they are clearly techniques and you should not go into paranoia. Maybe in future articles I will raise 'real' situations I am collecting many situations with many sentences of my work, but at the moment I do not have enough time to do something at that level.

Collapse
 
vankatakisyov profile image
Ivan

The nested ternary hurts my brain, despite that, the article has a lot of gems, thanks guys!

Collapse
 
damxipo profile image
Damian Cipolat

hahaha yes is hard to process I know, but are just examples, the idea is to open the brain to new alternatives some very nice other heavy to understand.

Collapse
 
jankowskij profile image
JJankowski

I totally agree that nested ternaries are a pain to read.

Also, the itemDropped sample looks very "magical" to a person not familiar with such paradigm.

Collapse
 
damxipo profile image
Damian Cipolat

Well the idea is to provide reason to star going deeper in JS.

Collapse
 
mattdimu profile image
Matthias Müller

While you've shown some nice alternatives for IF, most of these examples feel less readable after being refactored. Especially chaining the ternary operator should be avoided IMHO.

Collapse
 
damxipo profile image
Damian Cipolat

Yes in the refactor, some of semantic is lost. The chaining ternaries is the most extreme. Consider this things as example you can chose use it or not.

Collapse
 
diek profile image
diek

Well well, i think we should program in human language, you are not doing it with this examples xD

Collapse
 
damxipo profile image
Damian Cipolat

Is the problem with the functional paradigm, is less semantic in comparation with oop.

Collapse
 
kalium profile image
kalium.xyz

You can do functional fine with a more verbose language (e.g. rust allows for assignment with the result of an if statement), this is just a design choice of the language.

Having a more verbose language is not inherently bad nor are if statements not part of a proper functional language.

Thread Thread
 
mateiadrielrafael profile image
Matei Adriel

Yep, most fp langs consider if statements simple expressions, so you can pass the results around

Collapse
 
metalmikester profile image
Michel Renaud

I've had to deal with "overdone" uses of the ternary operator. I was not pleased.

Collapse
 
damxipo profile image
Damian Cipolat

The point of replacing the elseif with ternary nesting is hard to read. Another way could be to use a single level of if. but the idea of ​​the article was to program without using a single if. I am open to see other devs as they would raise this.

Collapse
 
mbuechmann profile image
Malte Büchmann

Very nice article. But I think I found one catch:

The rewrite in the second example for ternary operators makes the code very unreadable. The cumbersome part of this example are the else statements. If you use guard clauses if statements, the code becomes much more readable and concise:

function customerValidation(customer) {
  if (!customer.email) {
    return error('email is require');
  } 
  if (!customer.login) {
    return error('login is required');
  }
  if (!customer.name) {
    return error('name is required');
  }
  return customer;
}
Enter fullscreen mode Exit fullscreen mode

In this case, you can even remove the curly braces:

function customerValidation(customer) {
  if (!customer.email)
    return error('email is require');  
  if (!customer.login)
    return error('login is required');
  if (!customer.name)
    return error('name is required');

  return customer;
}
Enter fullscreen mode Exit fullscreen mode

Now both versions are much more readable and the reader can understand the intent of the written code much better: catch errors and return them.

Collapse
 
damxipo profile image
Damian Cipolat

thanks for comment!

Collapse
 
macarie profile image
Raul • Edited

The article is nice, it gives some cool alternatives that not everyone might know about, a handful of which we use at work.

As others said, tho, some are harder to read.

A little side note about #4 and #5: it would be better to re-use the object and not to create it every time a function is called (eg: creating it inside array.map(), etc), it slows down your code a lot!

I'm saying this because it might not be obvious and also because I've had to refactor some code that was doing the exact same thing after I got hired 😊

A better approach would be to do something like this instead:

const createDogSwitcher = () =>{
  const objectSwitch = {
    border: 'Border Collies are good boys and girls.',
    pitbull: 'Pit Bulls are good boys and girls.',
    german: 'German Shepherds are good boys and girls.',  
  }

  const defaultString = 'Im the default.'

  return (breed) => objectSwitch[breed] || defaultString
}

const dogSwitch = createDogSwitcher()

dogSwitch('border xxx')

// Now use `dogSwitch` inside `map()`
Enter fullscreen mode Exit fullscreen mode

If you want even better performance out of this, you can use a Map() instead of an object 🖖🏼

Edit: I think you have two #5s 🤔

Collapse
 
damxipo profile image
Damian Cipolat

thanks for comments! the idea was make an arrow function that return an object. I had thought about this option too, it is more semantic but much more code. It is in each developer as you feel more comfortable.

Collapse
 
ezgif_com profile image
ezgif.com • Edited

TBH I think all of your examples are more readable with ifs, especially the nested ternaries. I would hate to see that in code I have to work with.

Ifs are very easy to read and understand, there is no performance problems with them. What do you gain from avoiding ifs?

Collapse
 
uuykay profile image
William Kuang • Edited

No way is nested ternaries easier to read than if/else if statements. Teraries should be used for if/else statements only. Nesting them is insane. It reminds me of if/else if operations in Excel.

Collapse
 
damxipo profile image
Damian Cipolat

Yes is similar, the Excel formulas are something like fp

Collapse
 
seanog profile image
Sean O'Grady

what do you mean by this?

Thread Thread
 
uuykay profile image
William Kuang

If you've ever had to write a conditional in Microsoft Excel, you'll be familiar with this. Check out this article to see what I mean: exceljet.net/excel-functions/excel....

Excel doesn't have if/else if statements, it only has if/else which is the same as a ternary. Do you really think reading this is easier?
=IF(C6<70,"F",IF(C6<75,"D",IF(C6<85,"C",IF(C6<95,"B","A"))))

Thread Thread
 
damxipo profile image
Damian Cipolat

It is just a proposal, for me an excel formula is just as ugly as an if..else..elseif with nesting.

Collapse
 
gameoverwill profile image
Wilfredo Pérez

Hey Damian the last example is not working.

//Create function to different behaviour, same parameter call.
const qaSalary  = (base) => base+9000*0.10;
const devSalary = (base) => base+1000*0.60;

//Add function to the object.
const bob = {
  name:'Bob',
  salary:1000,
  job_type:'DEVELOPER',
  calc: devSalary
};

const mary = {
  name:'Mary',
  salary:1000,
  job_type:'QA',
  calc: qaSalary
};

//Same call.
console.log('Salary',bob.calc());
console.log('Salary',mary.calc());

the output is:

Salary NaN
Salary NaN
Collapse
 
damxipo profile image
Damian Cipolat

:o :O yes that true, I will fix it. thanks for tell me.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.