I've never become overly convinced that switch statements are that much cleaner than `if else if else if else if else`

twitter logo github logo ・1 min read

I sort of know that switch statements are typically considered a good idea when chaining together a lot of else if statements. I'm not against this refactor in any big way, but I've also never found it overly important.

I tend to find the behavior and syntax of if and else to be pretty consistent regardless of language compared with switch statements so reaching for if is easier, but even when chained doesn't seem that much less clean than switches.

I definitely agree that getting rid of if..else..else chains are worth refactoring away, but I haven't found that a switch statement actually improves the quality meaningfully.

Thoughts?

twitter logo DISCUSS (83)
markdown guide
 

That's why we never have switch in Python 😁

And, the dictionary is always a better and cleaner alternative for the ugly switch.

def my_switcher(x):
    return {
        'a': 1,
        'b': 2,
    }[x]


print(my_switcher('a'))
print(my_switcher('b'))
 

Elixir :)

def my_switcher("a"), do: 1
def my_switcher("b"), do: 2
def my_switcher(), do: 3

iex(1)> Example.my_switcher("a")
1
iex(2)> Example.my_switcher("b")
2
iex(3)> Example.my_switcher()
3
 
 

I quite like cond as well for certain situations as well.

 

Whenever I think about why I don't like a certain way a language does something, I realize it always goes back to my love for Python. That language has ruined me. haha

 
 

How would you handle a default case in this?

My though is to add || defaultValue at the end since a failure to match any property will return undefined. Thoughts?

 

That would be handy using a try except:

def my_switcher(x):
    switches = {
        'a': 1,
        'b': 2,
    }
    default = 3

    try:
        result = switches[x]
    except KeyError:
        result = default

    return result



print(my_switcher('a'))
print(my_switcher('b'))
print(my_switcher('c'))

I added the extra default variable just to make the code self explanatory.

EDIT: Bad solution, look at the other comments.

 

Really love this pattern. I have seen various approaches to the default case that vary across languages.

 

The main detractor with if statements is that you have to reduce your logic down to booleans, which can later be hard to trace through. But, switch statements do not go far enough to help the situation either. Reducing your logic down to integers or strings is not that much better of an abstraction. Both force you to reduce the expressiveness of your solution down to a primitive level.

This is a reason I am a big fan of Union types, which allow you to explicitly model your logically different cases along with their different related data. Combined with pattern matching, you can branch off those logical cases as-is. My favorite language which supports this is F#.

 

Kasey, I have a question for you since I’m interested in F#. I use union types all of the time in TypeScript, but I often “unpack” (for a lack of a better word) the union by using an if statement. It’s really convenient since TypeScript has excellent control flow analysis and therefore discriminates the union so you know that it is only the one type.

So do you still prefer the functional approach for discriminating the union even in languages that can clarify the resulting type?

(I’m sorry if this question isn’t more clear. I suppose I’m suggesting that if statements are fine since I think the left/right monad thing very confusing)

 

Typescript union types are a bit different from those in F#. There is a tradeoff made between them. Union declarations are more or less anonymous in Typescript, whereas F# requires you to create a Discriminated Union with explicit tags. So, the Typescript feels a little nicer on declaration. However, when you unpack the union type, I believe F# has the advantage. Using match you can get auto-completion of the cases and warning when all cases are not exhaustively matched. It also provides access to the value as the appropriate type.

Examples:

// TypeScript
function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
}

// usage
let foo = padLeft("padded", 4);
// "    padded"
let bar = padLeft("padded", "bar ");
// "bar padded"
// F#
type Padding =
    | Text of string
    | Spaces of int

let padLeft padding str =
    match padding with
    | Text prefix ->
        prefix + str
    | Spaces count ->
        String.replicate count " " + str

// usage
let foo = padLeft (Spaces 4) "padded"
// "    padded"
let bar = padLeft (Text "bar ") "padded"
// "bar padded"

So, I feel like the latter is a better expression of intent in the long run. Even though it requires an extra type declaration, the type declaration adds some value for readability. It is more clear what the code is going to do with "string | number" when labeled as Text and Spaces.

For F#, I also swapped the order of arguments so that padLeft would be chainable with other string functions using pipe (|>).

What a wonderful response. Thank you for being generous with your time to write that thoughtful response. You should post that as it’s own article so more people can benefit from it. :)

 

For me its about using the semantics of the language to communicate as much information as possible to other developers (and my future self). Its the same reason I prefer map, filter, reduce over for loops. By using switch instead of else if, I communicate a more specific action is taking place: dispatch over a single value. It's a small thing, but its also easy to do and makes my code a little more compact, so why not?

 

Having the fallthrough can be nice for certain cases when you want to keep your code DRY or want to avoid a ton of || operators in your if statement.

The switch operator can be much cleaner than if though, notably for small return statements. For example:

switch (x) {
  case 'a': return 1;
  case 'b': return 2;
}

is much cleaner than

if (x === 'a') {
  return 1;
} else if (x === 'b') {
  return 2;
}

Of course, it does come down to personal taste, but since languages provide the operator in the first place there's obviously a reason for it.

 

I don’t think fallthrough in a switch case is ever appropriate due to the potential for buggy side effects. I describe that in this article and I’d love to hear your thoughts on if you agree: dev.to/cubiclebuddha/is-defensive-...

 

I absolutely agree with your point. default/else cases should be used for error handling in most instances.

However, I should have clarified, so the fault is on my behalf, but when I said "switch 'fallthrough'", I meant when you have a situation like the following:

switch (x) {
  case 'a':
  case 'b':
    return 1;
  default: throw(); break;
}

Here, it's easier (for me, at least) to see which possible values return 1 than this:

if (x == 'a' || x == 'b') {
  return 1;
} else {
  throw();
}

I'm probably missing another thing you can do with if statements to make it more clear. But, after all, this is #healthydebate... So tell me how I'm wrong! :P

Those examples your provided look great to me. I just don’t like when code assumes that there will never be a new case. I explain that a little better in my article. But it looks like you’re covering the known cases. So great work! :)

 

If you're returning then you don't even need the else.

if (x === 'a') {
  return 1;
}

if (x === 'b') {
  return 2;
}

Actually looks a bit tidier to me and still very explicit.

 

Ah, true. I'm an idiot.

But for me, I still prefer the inline look of the switch statement. Again it's up to preference though.

 

Depends on the language, but for safety critical code, multiple function exits are typically frowned upon.

Nah. Exit early. Makes the code cleaner and clearer.

Gotta respectfully throw out a dispute. I've heard this statement used to justify multiple returns before... Looks like

if (x) {
    return a + x
} else if (y) {
    return a + y
} else {
    return a
}

With the justification that it avoids evaluating the additional conditionals if the first proves true.

I just don't think the performance loss is there barring some REALLY funky conditionals or some other code smell...

This is a rule that feels like it came from the old C days and just stuck around past it's expiration date, because that same logic, but expressed as

if (x) {
    a = a + x;
} else if (y) {
    a = a + y;
}

return a;

Is so much more readable and you can look at one line to know what's going to be returned, then you only have to debug/watch one thing, and it's just SO much cleaner... If I run into a problem with evaluating conditionals to get syntax that clean, then I feel like something else is wrong in the code... It's also more bug resistant and ensures that the method is doing ONE thing. Less chance that someone can come along and decide to start returning Strings instead of ints in one of the return statements or something wacky like that...

In the real situations I encounter the issue is that having early returns means being able to avoid huge indents. When reading code where the return is at the bottom of a long complex structure you are forced to parse the entire function to ensure there is no subsequent operation which influences the return value. Having one return value generally means having the return variable declaration at the highest level, and so it could potentially be modified through the function. You might intend for it not to be modified once set, but a later modification might overwrite it. A early return is syntactically a succinct expression of the intent. We are finished. Return this value now. I know that in the early days of programming there was a good reason not to return early, especially in the middle of a loop. Don't think there is any issue at all with this any more.

I could easily see if I'm dealing with a returnable statement over 20 lines where the return value is based on multiple conditionals or is impossible to keep in your head all at once, that return early and often would be a good backstop to make sure you know what is returned and when... And I HAVE run into code like this in the wild that I couldn't refactor entirely for stability, but I could make sure it was readable, and that did involve applying Exit Early.

With new code or things I could refactor, I would have to go with Uncle Bob's rule from Clean Code

The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that. Functions should not be 100 lines long. Functions should hardly ever be 20 lines long.

If you're dealing with multiple nested conditionals or the triangle of death where nesting is making the indents move out and out until your code looks like an arrow pointing off to the right, then the function is probably just too big and needs to be broken up for maintainability...

But now we're going to get so far off the topic we'll need to write a whole new topic to keep this up hahaha!

I broadly agree with the idea that functions should be small, but care should be taken not to break up logically sequential operations that really belong together just to comply with this principle.

More important to me is that a function should have one clearly defined purpose and not mash together a bunch of logically separate operations.

Switches tend to be used for doing multiple separate things based on some value. It is not always bad, but I always evaluate whether there is a better more extensible way to do it than a switch when I see it.

I'm not saying I've never used a switch, but it is generally not one I use unless there is a really good reason.

 

Switches as well as conditionals with many branches(else ifs) are generally indicative of a different issue all together.

Switches can be a good choice in the case of a clearly defined unchangeable list of things to check against, like days of the week(I pray to god there will only be seven in the future too). They are also sometimes good in the case that you actually have found a use case for the fall through effect you get when you don't use a break. Switches also are very hard to do in a way that enables defensive programming. It's easy for a switch to have 4 or 5 cases, then someone comes in and adds one, comes in and adds another, then another, until you have a giant mess of cases.

Generally, like @rossholloway94 mentioned, Enums are a much better fit. The way that @0xrumple showed in python can be done in many other languages that don't have real Enums, but is essentially an Enum as well. Alternatively, in certain use cases, something like a rule matcher and handler can be a better fit.

class Foo implements RuleHandler {
    public function canHandle(condition) : bool {
        if(condition === something) { return true; }
   }

   public function handle(thing) : Bar {
        return thing.thing() + thing.otherThing();
   }
}

Using a switch can also be indicative that whatever logic you're doing in the switch, is really something that should be on each class to implement which can be similar to the model above. Basically the Strategy design pattern.

//instead of this
function doThingsThing(thing: Thing) : void {
  switch(thing.val) {
    case 1 { thing.a + thing.b; break; }
    case 2 {thing.a * thing.b; break; }
    // more cases
    default { //this should never happen }
    // NEVER DO THIS TO A DEFAULT IF IT SHOULDN'T HAPPEN THROW AN ERROR
  }
}

//this
function doThingsThing(thing : Thing) {
  thing.doThing();
}

abstract class Thing { abstract function doThing() : Thing; }
class Thingy extends Thing { function doThing() : Thing {this.meh = 2; return this; } }
class OtherThingy extends Thing { function doThing() : Thing { this.meh = this.meh * 2; return this; } } 

The reason a switch never feels better than many else ifs is because they are basically the exact same thing. The kind of feel that gets hammered in a lot of beginner tutorials that explain switches is something like "If you have a lot of conditions, use a switch!" and also the examples show how to convert a thing with a lot of else if statements into a switch.

One more time for clarity:

  • Consider using an if in the case that:
    • There are a TRULY finite number of cases
    • The fall through logic will really help(though there are OOP ways of handling this too that I didn't show)
  • Some switch alternative candidates are:
    • Enums
    • Rule Handler matchers
    • Interfaces in classes
 

Enums are also good, but they might not fit in cases where there is no ordinal values.

Don't get offended, but the rule handler matcher / OOP ways to return a value reminds me of this meme:

deep-learning

 

Haha no offense at all. The rule handler is overkill in A LOT if not most scenarios. That said, it's for sure one of the patterns you would use instead of a switch so I included it. For the trivial example I have though, yeah way overkill. For the slackbot I made a while back I used it and it worked well. github.com/samuraiseoul/starbot/bl... and github.com/samuraiseoul/starbot/bl... are an example where it works well.

Aha... Heck yeah, this example is pretty solid 👌

 

Excellent point! Sometimes you may need lots of if statements, but more often than not, lots of ifs are a sign that your code is just too procedural. By abstracting away code in functions or classes you can often get rid of them.

 

Personally, I oppose switch statements because they force you to opt-in to break rather than breaking by default (i.e. use continue for fallthrough). I feel like every time I write a switch statement I forget a break on one of the cases. Then, I'm stuck exposing that fallthrough case during testing. In contrast, you don't really get that same ambiguity with if/else if/else.

 

In terms of performance, it never practically matters. For sake of the argument, switch is much faster if you have a large number of cases and slightly slower with small number of cases. The compiler is aware of the number of cases and will convert the switch statement into a constant lookup time hash table once it exceeds a threshold. But once again, for 99.999% of code, the difference between the two will never be meaningful.

I personally think that they are used differently, and that using them both is actually the best option. If/Else can be used very broadly, but with switch it's pretty niche. It's essentially a jump table that's useful when you have a ton of outcomes given some input. When I see a switch, I immediately know the class of problem it's trying to solve. With if/else, it could be much broader.

 

A good optimiser will use the optimal output for the size of the operation, irrelevant of the code statement.

 

I'm sorry but this is a really uneducated comment.

 

Considering it rapidly speeds up your conditional statements, I will always use a switch statement. A switch statement acts as though you are accessing an array via an index. Whereas an elseif statement has to process each conditional statement until it passes.

 

Depends on if scripted Vs compiled.

A compiler will optimise into either the conditions or a jump table depending on the code. Usually for both switch and if else

 

As someone who has never had the chance to jump into a compiled language, this is wonderful information! Thank you for the correction.

 

Of course this depends on the context of the application though. If I am checking against static values, then my previous statement is true.

 

Swift's switch is the only one that I think gets it right. It provides enough simplicity and usability to warrant use over if-else chains.

From docs.swift.org/swift-book/Language...

  • compund cases case "a", "A":
  • interval matching case 1..<5:
  • tuples switch point: case (0, 0)
  • value binding case let(x, y)
  • where checks
  • explicit fallthrough

Granted, each of these can still be done with if-else, I like the convenience of it all.

 

This here. switch in some languages (like F#, rust, and apparently swift- amongst others) has additional "powers".

In C# 8 you can also use it as an expression:

return x switch {
  0: something,
  // More
  _: whatever,
};

Otherwise it's main advantage is clarity for large numbers of options and optional fall through (double-edged sword that it is).

if (x == 0) {
  return something;
} else if (x == 1) {
// 10 more cases
else {
  return whatever;
}

// vs
switch (x) {
  case 0: return something;
  // 10 more
  default: return whatever;
}

Regardless what you do with your brackets/whitespace, the switch logic is clearly only about the value of x.

Also, historically compilers were more likely to turn it into a jump table rather than chain of branches.

 

I much prefer pattern matching like in F# to switch statements. Eevee did an interesting blog on the history of the switch statement. I dislike the fallthrough behavior and find it visually displeasing in a code base.

 

Thanks for the article! That was a quality read. Gave me a more clear explanation of why I don’t really like switch statements. Haha

 

I feel that with Java, if you have a switch statement, each condition can probably be modelled by an enumeration. And if that's the case, that can be refactored so that each enum type has a common abstract method to be called.

 

I like switch for when I return because you skip out on so much boilerplate over and over.

switch(something){
case "this":
    return 1;
case "that":
    return 2;
default:
    return 0;
}

Just feels much easier to type than

if(something === "this"){
    return 1;
} 
if (something === "that"){
    return 2;
}

return 0;

You have to keep typing something === over and over and I dislike that.

The speed etc is not important to me.

 

I often see that developers enjoy that approach until they run into a nasty bug down the line. I describe a bug like that here and I’d love to get your perspective on if my defensive coding approach I describe would be beneficial to you.

 

I am not a big fan, it forces me to use break a lot... Good old if... else never failed me so I am being faithful 😁

 

In kotlin we don't have a switch, we have a when operator:

fun doSomething(x: Int){
    when {
        x == 1 -> 
        x == 2 -> 
        else -> 
    }
}

It can take a parameter to match:

fun doSomething(x: Int){
    when(x) {
        1 -> 
        2 -> 
        else -> 
    }
}

And also you can put different stuff there…

fun doSomethingWithAFoo(foo: Foo){
    when(foo){
        is SubFoo -> {
          //…
        }
        foo.someProperty == someValue -> 
        else -> 
    }
}

in the case of is SubFoo you get auto-cast. So you can use foo as a SubFoo


For me more than 3 else-if will complicate the reading of the code and in some languages will get you a wrong scenario…

Like the kotlin syntax that actually doesn't have an else-if expression (yes, in kotlin is an expression) so you can end in weird places:

github.com/angryziber/kotlin-puzzl...

In kotlin else if(…) is actually: else { if() }

So that's one reason at least to prefer when over else-if in kotlin.

 

A while back in college, I read somewhere that when there are a similar number of conditions in an if-else block and a switch statement, the switch statement is faster.

Recently, during a FreeCodeCamp JavaScript section project, I had to use a bunch of conditions (I know it could have been done in a better way) in multiple functions. I decided to try using switch statements since that would apparently be faster. But as soon as I wrote the first function, I realized that although it would be faster, it looked ugly. I used good old if-else blocks in the other functions.

And although I had to think quite a bit about the order of the conditions, it looked a bit cleaner than the former.

For me, I would use if-else blocks instead of switch statements until it's absolutely necessary. This was btw my first use of switch statements in around 2 years since last I used them in college.

 

In C# I mostly use switches together with enums. Why?

First because if you use the switch code snippet it will generate you a switch-case with all enum parameters, e.g.:

public enum Values
{
    A,
    B,
    C
}

static void Switch(Values value)
{
    switch (value)
    {
        case Values.A:
            break;
        case Values.B:
            break;
        case Values.C:
            break;
        default:
            break;
    }
}

Second if you add a new case it will automatically suggest to use a value of the enum and typecheck the cases, e.g. you couldn't use a string.

 

For me it depends on the situation really. If I have a definite limited set of values I'm looking for, I may use a switch. To me, it can be easier to read the values down a column with it. If I'm looking for boolean conditions, the if will probably be the best. But just depends.

 

When I'm adding some new functionality to something the main thing I'm concerned with us maintainability, and a big part of that is code readability. If statements are easier to document, at least in my opinion, and they do a better job at being representative of their purpose and the current context of the code.
Switch blocks can certainly be contextual and representative, but it takes more effort to ensure that the context and logic are properly represented. I will say, that fallthrough can be very useful in the right scenarios.

 

I only use switch statements in two scenarios:

A: I'm using an Abstract Factory for something.

public class PokemonFactory {
  public static createPokemon (type: PokemonType): Pokemon {
    switch (type) {
       case 'pikachu':
         return new Pikachu();
       case 'charizard':
         return new Charizard();
       case 'squirtle':
         return new Squirtle();
       default:
         return null;
    }
  } 
}

B: If I'm writing reducers in redux

   switch (action) {
     case actions.FETCHING_PROFILE:
     case actions.FETCHING_PROFILE_SUCCESS:
     case actions.FETCHING_PROFILE_FAILURE:
     case actions.SUBMITTING_PROFILE:
     case actions.SUBMITTING_PROFILE_SUCCESS:
     case actions.SUBMITTING_PROFILE_FAILURE:
        return { ...state, profile: profileReducer(state.profile, action);
     default:
        return state;
  }

Imagine the OR pipes if that last one was made using if statements. Being able to match many cases at one time saves so much typing.

 

This entirely depends upon the language in which I'm working.

I'm pretty gun-shy about switches in C, especially when they are interacting with macros. Pre-processing magic is a nightmare to debug, especially when a typo accidentally eats the break.

Python I don't mind the ifelse design as much That's usually for data science-y code, so readability is rarely a high priority.

Go I'll use switch quite often. Between the assumed break and explicit fallthrough, it's just easier to read than the sibling in C. There's a few ways to make things easier and easily identified patterns. One of my favorite is a simple string check:

func something(multiple, required, variables string) error {
    switch ""{
        case multiple, required, variables:
            return errors.Errorf("all variables required %q %q %q", multiple, required, variables)
    }
    // brilliant logic here
}

I think Swift has a similar style switch statement, too, that is more useful that the default C switch design. (Could be wrong, I don't write Swift)

 

Since Java 8 I don't use switch case anymore for Java. I find it much more readable to create a functional interface, and add it to the enum. For example if you have an enum with different types of messages, and you want to correct topic for that type depending on some TopicConfiguration class. You can add a lambda config -> config.emailTopic to all the enums. Reducing it to just a single function call on the enum when needed.
In Rust there not even is enum, there is match which is more powerful. And you can use traits to get the same kind of construction as described for Java.

 

I hate switch. And I hate that most languages make fallthrough the default behavior for switch. I love that Swift decided to instead implement a "fallthrough" keyword, because I find that fallthrough is the cause of 99% of most switch problems.

In JavaScript, I find using objects is a good alternative most the time.

const days {
  monday: "Monday";
  tuesday: "Tuesday";
  // . . . etc
}

const today = days[monday];
 

Yeah while I reach for them often I usually only see benefits in a few cases

final switch(some enum)

This means all enum values must have a case, no default.

switch (arg) {
static foreach(...) 
Case t....

Now I can build my cases at Compile time and don't need to hand the first if or start with else.

But these are only things you find in D.

 

The main danger is the need to place a break; inline to prevent execution flowing through to the next block. Never found this very consistent with other conditional structures. Basically just an opportunity for a bug. It is also often abused, in that we don't want large conditional blocks of code. Usually there are better ways.

 

In ruby case statements feel less verbose, less prone to error since the intention is to validate a single statement against multiple outcomes and is more human readable.

With linting case statements are one way to still have compact code.

Everything has its utility.

if    status == :active     { '/active'}
elsif status == :incomplete { '/incomplete'}
elsif status == :suspended  { '/suspended'}
end

case status
when :active     then '/active'
when :incomplete then '/incomplete'
when :suspended  then '/suspended'
end
 

I only find myself using switch a lot in CoffeeScript, otherwise, I only use it if it reads better than if/else which is largely subjective.

weekPart = switch dayOfWeek
  when 1, 2 then 'Start of Week'
  when 3    then 'Mid week'
  when 4, 5 then 'End of week'
  else 'Weekend'
 

Syntactically, a switch statement express that you work with a limited set of option and only those.

For example, let say you have a mammal and you need to make a specific treatment depending if it's a cat, dog or a bird person (yes bird person are mammal laying eggs, just like the platypus).

As you got a limited set of supported mammals, I'll go for the switch as it is the syntax to express that you handle only that set.

match mammal {
  "dog" => pet_dog(),
  "cat" => judge_cat(),
  "bird_person" => make_dick_move()
}

In comparison, the "trick" with dictionary is making a structure wearing the meaning of a syntax element. I will not advocate against it nor will I support it. It may have some use but bear in mind the "semantic" trade off you are making.

It could be interesting to see if the switch statement is more efficient than a if else (and honestly it depends on so many option that I'm not even sure it will be relevant) but at the very least it tells other programmers "here we work only with those possibilities and only them".

 

For if statements with expressions, like call to functions, I prefer if/else chaining, also much cleaner IMHO.

For pattern matching and the likes, the switch just seems more natural to me, but only in languages such as functional ones (lisp-derivatives, ocaml, etc.), or languages with a functional approach on expression statements (kotlin, Java)

 

Switch statement runtime : O(1)
If/Else : O(n)

If your operating system drivers used if/else for polling/events, your network card would be running at a fraction of the speed it does. Sometimes performance matters. The original Linux scheduler was O(log n) which was replaced with two linked lists for a O(1) runtime since the log n scheduler was deemed too slow for process scheduling.

 

I barely use them. If ... else if ... else are the exact same thing to me. In the end they both translate to assembly to the same thing as well (compare and branch). An if/elseif clause is the same as a "case" clause, and the else is the same as a default clause. Period!

Classic DEV Post from Feb 23

5 Specific Actions to Achieve Success

Success is an entity that all human beings want and wish to achieve. It could be attaining success in the workplace, in business, in a relationship, in family matters, etc.

Ben Halpern profile image
A Canadian software developer who thinks he’s funny.