alternative approaches to multiple if else conditions

Viewed 205

I have multiple conditions to be checked and executed like below.

if (date == current_date && source === "s3") {
    table_name = "Table1";
} else if (date == current_date && source !== "s3") {
    table_name = "Table2";
} else if (date !== current_date && source === "s3") {
    table_name = "Table3";
} else if (date !== current_date && source !== "s3") {
    table_name = "Table4";
}

I think using switch statement doesn't make sense here since we are not evaluating case statement expression against switch expression.

So is it okay to go with multiple if else statements or any better alternative approach?

8 Answers

Your code is 100% a good option. It is just a bit hard to read. You can pull out common code into variable to make it more readable

var isCurrent = date == current_date;
var isS3 = source === "s3";

if (isCurrent && isS3) {
    table_name = "Table1";
} else if (isCurrent && !isS3) {
    table_name = "Table2";
} else if (!isCurrent && isS3) {
    table_name = "Table3";
} else {
    table_name = "Table4";
}

Other option is to use ternary operators

var isCurrent = date == current_date;
var isS3 = source === "s3";

if (isCurrent) {
    table_name = isS3 ? "Table1" : "Table2";
} else {
    table_name = isS3 ? "Table3" : "Table4";
}

It could be one big ternary, but it is a bit unreadable

var isCurrent = date == current_date;
var isS3 = source === "s3";

table_name = isCurrent ? 
    (isS3 ? "Table1" : "Table2") :
    (isS3 ? "Table3" : "Table4");

In this particular case, it might make for more logical understanding if we simplified it into a nested if statement:

if (date == current_date) {
  if (source === "s3") {
    table_name = "Table1";
  } else {
    table_name = "Table2";
  }
} else {
  if (source === "s3") {
    table_name = "Table3";
  } else {
    table_name = "Table4";
  }
}

This does do at most 2 logical comparisons and using program control instead, while achieving the same logical result, whereas yours will take up to... 8?

But at this point, it's mostly a nit-picking style issue, and the comments have a few good ideas. For example, if you were expecting this logic to grow, it would make a lot more sense to use a map/object to store this information.

And yes, a switch statement does not make much sense.

Here's my application for "the most over-engineered solution" contest:

const bools = [
  date === current_date,
  source === "s3",
  // more?
];

const mask = bools.reduce((x, e) => x + +e, "");
// [ false, false ] --> 00
// [ true,  false ] --> 10
// [ false, true  ] --> 01
// [ true,  true  ] --> 11

switch (mask) {

  case "00":
    table_name = "Table4";
    break;

  case "01":
    table_name = "Table3";
    break;

  case "10":
    table_name = "Table2";
    break;

  case "11":
    table_name = "Table1";
    break;

  default:
    // noop
    break;

}

Here could be another mask to switch over if you prefer, numerical this time:

const mask = bools.reduce((x, e, i) => x + e * Math.pow(2, i), 0);
// [ false, false ] --> 0
// [ true,  false ] --> 1
// [ false, true  ] --> 2
// [ true,  true  ] --> 3

Otherwise, just use your good ol' ifs :)

How about storing the conditions in a JSON structure? Like this...

hash = {"Table1current":"Table1", "Table1notcurrent":"Table4", etc....};

Then to access the value you want, just do...

table = "Table1";
current = date == current_date ? 'current' : 'notcurrent';
table_name = hash[table + current];

That way, your conditions are represented as a JSON structure, which has more advantages:

  • Readable by people without programming skills.
  • Readable, verifiable, and confirmable by computer parsers.
  • JSON files can be stored outside of your code, and only loaded by the JS when necessary.
  • By sectioning off data from code, people can work on the data without affecting code, and vice versa.

If you want a cleaner abstraction, you can try using a pattern-matching library such as tailored.

const { wildcard, clause, defmatch } = require('tailored')

const _ = wildcard()

const nonCurrentDate = new Date(0).toISOString()
const currentDate = new Date().toISOString()

const tableName = defmatch(
    clause([ currentDate, 's3' ], () => 'Table1'),
    clause([ currentDate, _    ], () => 'Table2'),
    clause([ _          , 's3' ], () => 'Table3'),
    clause([ _          , _    ], () => 'Table4'),
)

console.log(
    tableName(currentDate, 's3'), // Table1
    tableName(currentDate, '!!'), // Table2
    tableName(nonCurrentDate, 's3'), // Table3
    tableName(nonCurrentDate, '!!'), // Table4
)

Try on RunKit

You could generate the table using bit mask like so:

let getTable = (date, source) => {
    let currentMask = (date === current_date) ? 0 : 2; // turns on twos bit if not current date.
    let s3Mask = (source === "s3") ? 0 : 1; // turns on ones bit if not s3.
    let idx = (currentMask | s3Mask) + 1;
    return 'Table' + idx;
}

let current_date = 1; // hard code current date for testing.

console.log(getTable(1, "s3"));
console.log(getTable(1, "something"));
console.log(getTable(2, "s3"));
console.log(getTable(2, "something"));

Here’s about as small as you can get it, if brevity is your goal.

var table_name = (date == current_date && source === 's3') ? 'Table1' : (date == current_date && source !== 's3') ? 'Table2' : (date != current_date && source === 's3') ? 'Table3' : 'Table4';

Generally it is OK to go with multiple if else statements. As usually, the right answer depends on the context (number of contions, code inside ...)

I disagree that switch strictly does not make sense. The switch statement can make sense (depending on the context, of course :-), using the switch(true) construction:

switch (true) {
    case date == current_date && source === "s3":
        table_name = "Table1";
        break;
    case date == current_date && source !== "s3":
        table_name = "Table2";
        break;
    case date !== current_date && source === "s3":
        table_name = "Table3";
        break;
    case date !== current_date && source !== "s3":
        table_name = "Table4";
        break;
}

In javascript, cases can be not only static values, but also expressions.

If choosing this approach, be aware that the result is strictly compared to true, so not all "truthy" values would work.

I some cases it may be more readable this way.

Related