How and when to use Ruby case statements
Using the case expression in Ruby is a great way to write conditionals in a clear, and succinct way.
Despite that, it’s not hard to encounter convoluted if constructs in places where refactoring to the case statement would result in huge improvements. Let’s learn more about the case expression and when it’s best to use it.
Expressing logic simply
Consider this piece of code:
number = 6
if number > 0 && number <= 3
'low value'
elsif number > 3 && number <= 7
'medium value'
elsif number > 7 && number <= 10
' high value'
else
'invalid value'
endIt seems like quite a complicated way to express a pretty straightforward logic, doesn’t it? Still, I bet that you have seen a construct like that many times. I know I have.
This logic could be greatly simplified with the case expression:
case number
when (0..3)
'low value'
when (4..7)
'medium value'
when (8..10)
'high value'
else
'invalid value'
endWe could also use the then keyword to make this even more succinct:
case number
when (0..3) then 'low value'
when (4..7) then 'medium value'
when (8..10) then 'high value'
else 'invalid value'
endThis code effectively works like:
if (0..3) === number
'low value'
elsif (4..7) === number
'medium value'
elsif (8..10) === number
'high value'
else
'invalid value'
endAs you can see, the case statements are most powerful when our logic is concerned with a value of a single object (the number in the above examples), which is provided next to the case keyword.
What comes after each when acts as a pattern. It doesn’t have to be a Range necessarily - this would work with any object.
Similar to how the #grep method behaves, the case expression results in invoking the #=== method on the given patterns.
In fact, the #=== method in Ruby is often called the “case equality”, as it’s mostly known for being used in case statements.
You can consult the Ruby documentation and look into how the #=== method is implement in various classes. On the Object class, it works the same as the regular equality method, the #==. It’s then up to descending classes to implement their own interpretation of the “case equality”.
Patterns in the case statements
Let’s look at some examples of objects being used as patterns in the case statements.
The case statement with integers as patterns:
case number
when 0 then false
when 1 then true
endWith strings:
case answer
when 'Y' then true
when 'N' then false
endAnd with regexes:
case string
when (/^a/) then 'starts with an A'
when (/^z/) then 'starts with a Z'
endYou can of course mix the types of patterns within one statement:
case number
when 0 then 'zero'
when (1..3) then 'low value'
when (4..7) then 'medium value'
when (8..10) then 'high value'
endOne last tip. You can provide multiple patterns for each when. This would effectively mimic the logical || operator, with each pattern being compared against.
To illustrate this, let’s expand on a previous example:
case answer.downcase
when 'y', 'yes' then true
when 'n', 'no' then false
endUnder the hood, this works just like:
if 'y' === answer.downcase || 'yes' === answer.downcase
true
elsif 'n' === answer.downcase || 'no' === answer.downcase
false
endSummary
I hope that you find this quick recap of the case expression useful. How often are you using case statements in your projects? Please share in the comments.
If your Ruby project suffers from slow CI build times, consider using Knapsack Pro to improve the productivity of your team.