DEV Community

Muhammad Haseeb
Muhammad Haseeb

Posted on • Updated on

Do we even need if/else?

Flow control is one of the first things we all learn as programmers.
We are going to learn about some of the alternatives we have, that from my point of view are generally more cleaner and safer.

clean code

Lets take a simple example to start with;

if/Ternary

const isWeekend = (day) => {
  let message;
  if (day === 'sunday') {
      message = "Its a weekend";
  }
  return message;
};
Enter fullscreen mode Exit fullscreen mode

We have a function isWeekend which takes a dayand returns if its a weekend or not. Now this has an issue, but JavaScript itself doesn't give us any kind of error. We didn't return any message if its not sunday. So we can do something like this or add an else block:

const isWeekend = (day) => {
  let message = 'Its a working day :(';
  if (day === 'sunday') {
      message = "Its a weekend";
  }
  return message;
};
Enter fullscreen mode Exit fullscreen mode

Now, as As the title says, do we even need if for this simple conditional block? No, we can use a ternary instead.
So we can update our function isWeekend like this:

const isWeekend = (day) =>
  day === "sunday" ? "Its a weekend" : "Its a working day :(";

// or

const isWeekend = (day) =>
  'Its a ${ day === "sunday" ? "weekend" : "working day :(" }';

Enter fullscreen mode Exit fullscreen mode

Advantages of ternaries over ifs:

  • It forces to cover both if and else case.
  • Less code footprints.
  • More readable.
  • Another big advantage is that we can initialize a constant based on condition i.e.
const a = condition? 'value1' : 'value2';
Enter fullscreen mode Exit fullscreen mode

We cannot achieve this using if else and will have to use let instead of const.

if/Switch

But what if we have to cover multiple conditions. Instead of using multiple ifs we should use the switch statement. Let take the same example, this time we need to have a condition for all the possible days i.e.

// multiple case switch program
  switch (day) {
    case "monday":
    case "tuesday":
    case "wednesday":
    case "thursday":
    case "friday":
      return "Its a working day :(";
      break;
    case "saturday":
    case "sunday":
      return "Its a weekend";
      break;
    default:
      return "thats not even a day";
      break;
  }
Enter fullscreen mode Exit fullscreen mode

Plain Objects and the ??

We can even use plain objects and the nullish operator.

const daysMap = (day) =>
  ({
    "monday": "Its a working day :(",
    "tueday": "Its a working day :(",
    "wednesday": "Its a working day :(",
    "thursday": "Its a working day :(",
    "friday": "Its a working day :(",
    "saturday": "its a weekend",
    "sunday": "its a weekend",
  }[day] ?? "thats not even a day");

const isWeekend = ( day ) => daysMap(day);
Enter fullscreen mode Exit fullscreen mode

Those who are not familiar with the ?? operator, it checks either it has a value or nullish (null or undefined). If day is nullish, then we use defaultValue, it not, use the value itself.

Conclusion:

There might be some cases where we have to use if/else. But, in my opinion, we can use the alternatives in most cases.

Any thought??

#coding #softwareengineering #productivity #cleancode #codingtips #javascript #webdev #devlife #programming #computerscience

Top comments (16)

Collapse
 
joelbonetr profile image
JoelBonetR 🥇 • Edited

Yes of course we need if/else statements.

  • Using ternary operator you simply chain if - else if statements the bad way under the hood which is not always the best option. It is good for a single comparison with a fallback or 2 as much.

foo === 'x' ? doSomething() : foo === 'z' ? doSomethingElse() : fallback()

equals:

if (foo === 'x') {
  doSomething();  
} else {
  if (foo === 'z') {
    doSomethingElse();
  } else {
    fallback();
  }
} 
Enter fullscreen mode Exit fullscreen mode

you can quickly spot the problem here, specially when chaining more options.
On the other hand if you only need to handle an action if something evaluates to true, there's no point on using a ternary:

if (foo === true) 
  doWhatever();
Enter fullscreen mode Exit fullscreen mode

cannot be translated into a ternary, simply because you don't need to do a different thing in case the expression evaluates to false, it would be somewhat weird (and wrong) to do it like:

foo === true ? doWhatever : '';
Enter fullscreen mode Exit fullscreen mode
  • Using switch case you only provide an action, also not valid for every operation. Several times you need to ensure multiple conditions. Simple example: needing to check if a user has a paid account and then checking if this account is a premium account or a regular paid one, what you do? chaining switch statements? that would be a mess. Chaining a ternary after the switch is also not an option for the last example in ternary concerns.

*As side-note, switch statements are harder to optimise by the JIT as well, in comparison with if-else statements.

  • To use the Nullish coalescing operator ?? operator you don't even need an Object, i.e.
const foo = null ?? 'default string'; 
Enter fullscreen mode Exit fullscreen mode

is valid as well but as you can imagine, that does not fit for any case.

So yes, we still need if else statements and they do the job pretty well in many cases.

Collapse
 
pengeszikra profile image
Peter Vivo • Edited

Depend on format, imho much better readable than if plus ternary give a result, do not need use let or var

const result = foo === 'x'
  ? doSomething() 
  : foo === 'z' 
    ? doSomethingElse() 
    : fallback()
  ;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
joelbonetr profile image
JoelBonetR 🥇 • Edited

I can't understand why y'all focus on readability in the first hand. Even Uncle Bob told us that the first half of a Dev's job is to make things work and the second half is to make it clean and readable.
You need to check first that your code covers the use cases and it's right (in the functional sense) and then you can re-write it on a more readable way (if any).

I'll set a funny working example on how to hash a string in JS using the Bitwise OR assignment in combination with the Left Shift Operator :

String.prototype.hash = function () {
  'use strict';
  var h = 0, i;
  if (this.length === 0) return h;
  for (i = 0; i < this.length; i++) {
    h = (h << 5) - h + this.charCodeAt(i);
    h |= 0;
  }
  return h;
}
Enter fullscreen mode Exit fullscreen mode

now you can do myString.hash(). Enjoy.
If you're too afraid of ES releasing a hash() method you can use it as function as well

hash(str) { 
  'use strict';
  var h = 0, i;
  if (this.length === 0) return h;
  for (i = 0; i < this.length; i++) {
    h = (h << 5) - h + this.charCodeAt(i);
    h |= 0;
  }
  return h;
}
Enter fullscreen mode Exit fullscreen mode

and call it like myString = hash(myString)

It works, i states for index and h states for hash.
You can change the variable names if you want to "index" and "hash" but can it be more readable than that? Let's let this question on air so you can answer with your ideas.

My guess is that unless you know how those operators work, you'll not fully understand it, on the other hand if you know'em, it will be obvious to you.

Thread Thread
 
pengeszikra profile image
Peter Vivo • Edited

I think readability help take look our code even few month later.

Imho your hash algorythm looks like this:

const hash = str => [...str]
  .map(chr => chr.charCodeAt(0))
  .reduce((acu, code) => ((acu << 5) - acu + code) | 0, 0)
  ;
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇 • Edited

That's beautiful, and it can be provided as string method like:

String.prototype.hash = function () {
  return [...this]
  .map(chr => chr.charCodeAt(0))
  .reduce((acu, code) => ((acu << 5) - acu + code) | 0, 0)
}
Enter fullscreen mode Exit fullscreen mode

This last one adds the requirement of understanding map and reduce and the one above the knowledge of basic if operator and for loop

That's just what we were talking about. Either the one in my post above or yours reduced example will be easy to understand if you know the operands but hard a.f. if you don't.

Readability is objective till some point where it turns subjective (by the knowledge and experience PoV) and that's the main reason of this cyclic discussions imo 😆

Someone whith basic or zero understanding on map would say yours is shit and someone with no knowledge of bitwise operators would complain of mine as well. The result in terms of readability can vary but none of those examples have a single thing wrong in readability terms, it has more or less lines of code (which is never equal to readability) and different operators or functions to reach the same. Which is more readable? Some will say 1 some will say 2. The important thing here is that both provide (unless I miss something) the exact same result for a same entry value.

Thread Thread
 
pengeszikra profile image
Peter Vivo

That is the reason to know more about .map .filter .find and .reduce functionality in js. After basic one like if/else, for, while, switch. These functionality is lead to functional programming direction.

Any way

String.prototype.hash =
Enter fullscreen mode Exit fullscreen mode

means you declare one global dependency on your code. I think much better to forget this type of work. Because this declaration influence your team whole application.

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇

It will be available just when you import the file containing it, not less not more. It just depends on the project architecture. The only concern about setting string (or other data structure) methods like this is the possibility of ES adding a method with the same name and thus having to refactor it everywhere it's used (or deleting it if it's provided along the language)

Collapse
 
joelbonetr profile image
JoelBonetR 🥇 • Edited

Having a chained ternary is not always wrong. Chaining them more than 2 or 3 times are usually wrong because you'll rely on checks that are dependant on each other and you'll probably need different chained ternary blocks to reach the expected use cases so it's better to use if/else but

foo === "x" ? doSomething : foo === "z" ? doSomethingElse : fallback;
Enter fullscreen mode Exit fullscreen mode

There's nothing wrong here as long as this covers your use cases properly. If you don't need the fallback then this is wrong but if this is just what you need then go ahead with that, we should be fine.

const otherCases = foo => (foo === "z" ? doSomethingElse : fallback);

const example = foo === "x" ? doSomething : otherCases(foo);

example(value)();
Enter fullscreen mode Exit fullscreen mode

This is supposed to be equivalent to the code above but instead you add a function declaration and a variable to reach... pretty much the same?
This is absurd. An extreme lack of knowledge about logic gates and inefficient way of working just because "you like how it looks".

Then here

foo ? doSomething() : undefined;
Enter fullscreen mode Exit fullscreen mode

Why should you state "undefined" just because foo is falsy?
You are not even setting this value into a variable to check later (it can be ok depending on the use case). instead you're just saying:

if (foo) doSomething();
else undefined;
Enter fullscreen mode Exit fullscreen mode

The question now is... undefined what? What's the point? What should we do with this undefined?

Again your starter point is "I like how it looks" not an understanding on how things work below.

Collapse
 
aschwin profile image
Aschwin Wesselius

Thanks for the examples in JavaScript.

I thought you were going to show the use of monads in JavaScript, LOL. Because every time I see a post about omitting if/else, switches etc. they introduce the usage of monads. Which are maybe better, computation and logic wise, but a hard topic to master.

Collapse
 
totally_chase profile image
Phantz

The irony there being, to implement monadic binding to abstract away branching, you need branching in the first place :)

Since we did bring up monads, the entirety of category theory must now follow. Selective applicative functors showcase the "branch"ing effect abstracted away into the functor. Which is ofcourse a realization of strength and costrength applied elegantly. The application itself, of this abstract concept in practice, requires laziness, but that's uninteresting.

I kid, I kid. Not "kid" in the sense that everything I said above is nonsense (it isn't), but rather, in the sense that all of it should be nonsense :)

(Haskellers, please don't kill me with your arrows)
(Non-Haskellers, please don't crucify me for unnecessarily bringing up category theory)

Collapse
 
gauravchandra profile image
Gaurav Chandra

Although this is fine for concise code but it is a nightmare when it comes to readibility. Not every dev is on the same level and it is imperitive that the code be readable. With all these bundlers in js ecosystem, this code will anyways be coompacted.

We should not forget that right now humans are writing the code and not machines so humans need to understand that part. The code anyways boils down to machine code at the time of compilation/transpilation. And I completely agree with @joelbonetr .

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
haseeb1009 profile image
Muhammad Haseeb

yeah that was something opinionated as I mentioned and any thoughts will be appreciated.
Thanks for highlighting the typo :)

Collapse
 
haseeb1009 profile image
Muhammad Haseeb

Very well explained @lukeshiru
I think moving nested conditions away make it more explicit and readable and yet compact than multiple if else

Collapse
 
barakplasma profile image
Michael Salaverry

I was hoping for an explanation of polymorphism in this article to replace if/else
See refactoring.guru/replace-condition... for more details on it

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