# Case Statement Magic

> Last updated: 2018-01-11

Earlier this week, a conversation on the Seattle.rb Slack reminded me of one of my favorite hidden gems in Ruby, the `case` statement. Most languages have syntax for multiway branching. Java calls it `switch`. Lisp uses `cond`. Ruby calls it `case` which relates back to the [mathematical roots](https://en.wikipedia.org/wiki/Switch_statement#History) of this construct. Ruby’s implementation of this feature is particularly flexible, and many folks don’t realize all the things you can do with it.

## Testing Values

Here’s a straightforward example of a case statement the way we typically think of them.

```
case my_var
when 1
  puts "one"
when 2
  puts "two"
else
  puts "too big!"
end
```

Ruby evaluates the variable `my_var` against each of the conditions listed. If the condition is true, the associated code branch is executed. But you can put more than a single value after the when. In this example, I use several different values separated by commas to specify each branch.

```
case my_var
when 1, 3, 5
  puts "odd"
when 2, 4, 6
  puts "even"
else
  puts "uh-oh"
end
```

And in this example, I use ranges instead of explicit values. Once a matching “when” is found that branch is executed and the rest are skipped over. There is no “fall-through”.

```
case my_var
when (1..5)
  puts "Left Hand"
when (6..10)
  puts "Right Hand"
end
```

You can also use regular expressions.

```
case my_string
when /[a-z]+/
  puts "lower"
when /[A-Z]+/
  puts "UPPER"
end
```

## Adding More Flexibility

The above shouldn’t look too bizarre to folks who know Java or C. The syntax is a bit different, but the basic use case of testing a variable against several possible values is the same. This isn’t how I normally use a case statement in Ruby though. Most of my case statements look something like this:

```
case 
when x % 3 == 0
  "Multiple of 3"
when y.odd?
  "Y is Odd"
when z.even?
  "Z is Even"
end
```

In Ruby the expression after the reserved word `case` is optional. When used like this the case statement is just a different way of doing multiple if/elsif/else lines. It is also analogous to `cond` in Lisp, which works the same way. I prefer to use case over if/elsif/else. I like that the whens and conditions line up vertically. In my brain, case means picking among more than two alternatives, and so semantically case makes more sense when I have a multiway conditional. Using case for multiway conditionals is so habitual for me that I have to think for a second about how Ruby spells “else if” if I’m using “if” for branching.

## Our Friend `===`

No discussion of case would be complete without discussing `===`, often called “case equality”. `===` is also called the “is\_a?” operator. `mammal === pangolin` would be true because a pangolin is a mammal. Note that the order is backwards of what we’d normally think in Ruby, it isn’t `mammal.is_a? pangolin`, but `pangolin.is_a? mammal`. The case statement uses `===` when determining which branch to follow and this allows for statements like this:

```
case my_var
when Numeric
  puts "numbers"
when String
  puts "String"
when Regexp
  puts "Reg Ex"
end
```

But `===` can be overridden by any class and some classes do interesting things. Range defines `===` as an alias for `include?`. Regexp defines `===` as an alias for `match`. While these don’t make sense with the ‘is\_a?’ explanation they do make sense if you think in terms of sets. `===` is set inclusion. For example `a === b` is true when b is in the set defined by a. In that context `Range#===` being defined as `include?` makes sense. `when (1..5)` is testing whether the variable is part of the set of things defined by `(1..5)`.

But there are some even more unusual definitions of `===` in the Ruby Standard Lib. In Brandon Weaver’s [Triple Equals Black Magic](https://medium.com/rubyinside/triple-equals-black-magic-d934936a6379) I learned that Proc defines `===` to be call and `IPAddr` defines it to be subnet inclusion. This means we can do things like this:

```
case my_var
when Proc.new { |n| n.odd? }
  puts "Odd number"
when Proc.new { |n| n.even? }
  puts "Even number"
end
```

I probably wouldn’t use this in production code, just like I won’t call [`next` with a return value](https://medium.com/@ryanflach/next-and-break-in-ruby-4bf9daf5322d) in production code. While both of these are valid Ruby, they aren’t idiomatic. Code that isn’t idiomatic is harder to understand and therefore maintain. So I’ll continue marveling at how awesome Ruby is but I’ll avoid using Procs in case statements unless the alternative is even less readable.

Please enable JavaScript to view the [comments powered by Disqus.](https://disqus.com/?ref_noscript)

 ![Aja Hammerly](/img/traditionalheadshot.jpg)

Aja Hammerly

Seattle, WA

### library\_books Series Index 

- [Developer Relations](/series#developer-relations)4
- [Machine Learning Basics](/series#machine-learning-basics)3
- [Battleship](/series#battleship)5
- [Talks I Love](/series#talks-i-love)6
- [Algorithms with Auntie Aja](/series#algorithms-with-auntie-aja)3
- [Ruby and GCP](/series#ruby-and-gcp)5
- [Playing with Data](/series#playing-with-data)3
- [Kubernetes & Rails](/series#kubernetes-rails)3
- [Learn To Code Resources](/series#learn-to-code-resources)3
- [Testing Anti-Patterns](/series#testing-anti-patterns)5
