DEV Community

Pavel Keyzik
Pavel Keyzik

Posted on • Originally published at pavelkeyzik.com

The Best Way to Reduce Complex if-else Statements

The problem

Have you ever had a really large if-else statement, with many variables, just to cover all cases. Let's say you have some message you want to show to the user, based on many inputs. How would you implement it? Probably, you'll start with if-else statement, trying to cover as many cases as possible. It's totally fine, until you have too many inputs.

Every new input will increase the amount of cases progressively. For example, when you have 2 variables, it's just 4 cases, but when it's 3 variables, then you have 8 possible cases.

Possible situation

Now, imagine we build a website to order food and your restaurant provides delivery or pickup. You go to the website and see "At the moment, we have delivery issues, but you can order pickup instead".

Here's the list of possible input variables:

  • do we have delivery?
  • is delivery available today?
  • is delivery open now?
  • can I order for later?

Can you guess how large your if-else statement will be? It's huge! And it'll be quite hard to cover everything. What if we have delivery, but pickup is not available for today? What if we have some issues with delivery and pickup will be open in an hour? The amount of possible situations is 2^4=16 and it'll be impossible to maintain when number of inputs grows. But don't worry! The solution will blow your mind! At least that's what happened to me πŸ€ͺ

The solution

The idea of that solution is so simple. You need to build a table of truths (all possible variants) and based on that table, each row will have a unique key that you can use as "status code". In the table below, I convert boolean values to 1 (true) and 0 (false). That way for each case we'll have a unique combination of 1 and 0 that will be our unique key, like1101, and we could always convert this binary number to decimal number. For example, 1101 becomes 13, and that's why you'll see normal numbers in the status code column.

Status code Do we have delivery? Is delivery available today? Is delivery open now? Can I order for later?
0 0 0 0 0
1 0 0 0 1
2 0 0 1 0
3 0 0 1 1
4 0 1 0 0
5 0 1 0 1
6 0 1 1 0
7 0 1 1 1
8 1 0 0 0
9 1 0 0 1
10 1 0 1 0
11 1 0 1 1
12 1 1 0 0
13 1 1 0 1
14 1 1 1 0
15 1 1 1 1

Now what? That's the beauty of that solution, it's all. You just need to return status code and map this code to any message you want. Check code examples section, to see the implementation.

Code examples

In this section, I'll provide a code you need to write to do this. It's in JavaScript, but it could be written in any language.

// Restaurant configuration, all our input variables are stored here
const restaurantConfiguration = {
  haveDelivery: true,
  deliveryAvailableToday: true,
  deliveryOpenNow: false,
  canOrderForLater: true,
}

// This function takes configuration and converts all `true` and `false` values to status code
function getStatusCode(restaurantConfiguration) {
  const { haveDelivery, deliveryAvailableToday, deliveryOpenNow, canOrderForLater } =
    restaurantConfiguration

  // Note: don't change the order, or if you need to add more variables,
  // add them to the beginning of the array.
  // That way you won't break old status codes
  const binaryNumberArray = [
    haveDelivery,
    deliveryAvailableToday,
    deliveryOpenNow,
    canOrderForLater,
  ].map((isTrue) => (isTrue ? '1' : '0')) // [true, true, false, true] => ['1', '1', '0', '1']

  const binaryNumber = binaryNumberArray.join('') // '1101'
  const statusCode = parseInt(binaryNumber, 2) // Converts '1101' to 13

  return statusCode
}

// This is your mapping between message and status code
function buildMessagesTable() {
  return {
    0: null,
    // ... cover any status code you need
    13: 'Delivery is available today, but it is not open yet, so you could order for later',
  }
}

function main() {
  // Get status code
  const statusCode = getStatusCode(restaurantConfiguration)

  // Get a table with messages by status code
  const messagesTable = buildMessagesTable()

  // Get message by status code
  const message = messagesTable[statusCode] || null

  if (message) {
    console.log(message)
  }
}

main()
Enter fullscreen mode Exit fullscreen mode

Summary

The only thing I want to mention about this solution is that it works great, especially when you have a bunch of inputs and when you need to cover a new case, you don't need to think about if-else statement just to cover your case, you find out the status code and add one line with the message you need. Less code, fewer bugs πŸ’ͺ

Did you like the article? Share it with your friends. I'd appreciate that πŸ™

Latest comments (8)

Collapse
 
blindfish3 profile image
Ben Calder

It's a really clever approach to matching the conditions; but in a real-world application I'm not sure you would ever combine all these conditions into a single if-else flow. I'd expect that they would manifest themselves in the user flow so that a response is given as close to the input as possible. For example the moment a user selects 'delivery' they're immediately made aware that it isn't possible; or presented with times when it is possible. If they select pick-up they're told an approximate wait time etc. You simply wouldn't wait until all options are completed before giving any feedback.

Collapse
 
pavelkeyzik profile image
Pavel Keyzik

In real world app I have 8 inputs parameters and based on the combination I show message to the user. I know it’s not usual and most applications don’t need something like this. But I had a problem in a real project and this solution works really nice for me. It’s easy to read, easy to maintain, and hard to break πŸ˜‚ Maybe the example is not really good, but should describe the concept. I didn’t want to share production code base with all 8 inputs and logic behind these inputs πŸ˜„

Collapse
 
ant_f_dev profile image
Anthony Fung

Neat idea - thanks for sharing!

I know the decimal codes are built up from the binary codes, but how do you feel about adding the table as a comment too? I don't mean in the example above (it's fairly clear); I just meant in real-world usage.

Collapse
 
pavelkeyzik profile image
Pavel Keyzik

You're right about it, in real-world usage you'll have to write some documentation. It could be a separate Markdown file, Confluence page, or whatever. I'd say it should be somewhere, where everyone from your team will have access to, as it can be used by designers, QA team, or even Product team. It could be as a comment in the code as well, if it makes sense for your team, but I feel like having a separate doc is much better, as you can share a link to the table with anyone. There is no right or wrong answer πŸ˜‚

Collapse
 
ant_f_dev profile image
Anthony Fung

Good point - the 'problem' with code comments is that they're pretty much exclusive to people who use the code directly. One thing that I've come across quite often is leaving a Jira (or other bug tracking system) ticket ID in the commit message. The ticket usually has more details/background on the task. That seems like a good candidate too.

Thread Thread
 
pavelkeyzik profile image
Pavel Keyzik

Absolutely πŸ‘

Collapse
 
hareom284 profile image
Zaw Zaw Win

Wow what a greate way you implement

Collapse
 
pavelkeyzik profile image
Pavel Keyzik

Thanks! Happy to see that people love it 😍