Hash Fetch Instead of If/Else
- 2 minutes read - 416 wordsConditional logic has its place, but often there’s a better alternative.
Today, we’ll look at a Ruby solution: a hash with .fetch
.
My thanks to Brian Dunn, who showed me the value of this technique.
Imagine you have a method that translates some data, a symbol for a role, into a presentation. A conditional is a popular solution here:
def display_name(role)
if role == :customer
'angler'
elsif role == :employee
'guide'
else
name.to_s
end
end
I think this method is much improved as a hash:
ROLE_MAP = { customer: 'angler', employee: 'guide' }
def display_name(role)
ROLE_MAP.fetch(role)
end
So, what have I got against conditional logic?
- It’s not very readable (subjective, but still). Conditionals don’t ’line up'
well, they use the awkward keyword
elsif
, they’re wordy (eight lines of code), they encourage sneaky things like early returns. It’s not code I can scan quickly, even an example as simple as this. - It doesn’t scale. Need a new role? Add two lines of code and another logical fork. Again and again.
- It doesn’t fail. If you don’t have a role defined, you must have a default or
this method returns
nil
. Passing thatnil
back the caller is going to cause problems.
Just use a switch, you say! I like switches. But beyond perhaps being a little more readable, they don’t solve the problems I mentioned above.
Consider a hash:
ROLE_MAP = { customer: 'angler', employee: 'guide' }
def display_name(role)
ROLE_MAP.fetch(role)
end
We start with ROLE_MAP
, a constant. This tells the reader: this is an
important thing, for mapping roles!
Into the method, we call .fetch
on our map. Fetch raises a KeyError
when
the first argument isn’t a key in the hash. If we don’t have a mapping, we fail
fast, rather than passing the problem along.
Additionally, we use can use second argument to .fetch
, a default value.
This allows our method to always show something, ideal for presentation.
ROLE_MAP.fetch(role, role.to_s)
So, why use the hash?
- I think it is more readable. If you know
.fetch
and you haven’t hiddenROLE_MAP
away, this is simple code. - It scales. Need a new role? Add a new key-value pair to the hash.
- I can fail fast.
.fetch
raises when it doesn’t have a mapping, or, you can use a default. Both are useful.
If you’re changing existing code, make sure that the legacy behavior is tested so you’re only changing behavior in a way you understand.
Use a hash and enjoy your more readable, more scalable, fail-fast code.