In Progress
Unit 1, Lesson 1
In Progress

Nil Conversions

Video transcript & code

Hey folks, I'm a bit short on time this week, so I've just a got a quick tip for you today. It has to do with getting rid of nil values.

Let's say we're trying to come up with a unique numeric ID for some object or other. We have a list of all the existing IDs, so a simple way to generate a new one is to just find the highest current value and increment it by one.

ids = [23, 48, 17, 6, 39]
new_id = ids.max + 1            # => 49

This works great, until one day we start from a blank list of current IDs. This time, it blows up.

ids = []
new_id = ids.max + 1            # =>

# ~> NoMethodError
# ~> undefined method `+' for nil:NilClass
# ~>
# ~> xmptmp-in30752pYg.rb:2:in `<main>'

This happens because when #max can't find any values, it returns nil.

ids = []
new_id = ids.max                # => nil

I often come across code that copes with situations like this by using an || operator to replace nil values with zero.

ids = []
new_id = (ids.max || 0) + 1     # => 1

There's nothing wrong with this code, but there's another way to do it which is both shorter and, in my opinion, better.

Ruby's nil value has built-in conversions to a number of basic types. One of them is to Integer. Tel a nil to convert itself to an integer, and you'll get 0 back.

nil.to_i                        # => 0

Of course, integers can also be sent the #to_i message, and they'll just return themselves.

23.to_i                         # => 23

What this means is instead of putting in some parenthesized conditional logic, we can just send the #to_i message to whatever #max returns. If it returns an integer, this will effectively be a no-op. And if it returns a nil, the nil will be converted to 0.

ids = [23, 48, 17, 6, 39]
new_id = ids.max.to_i + 1     # => 49

ids = []
new_id = ids.max.to_i + 1     # => 1

This is shorter code, and we've replaced a conditional with polymorphism. Which is nearly always an improvement, in my book.

nil has lots of these conversions defined, including to strings, floats, arrays, and more. If you're currently switching on whether an object is nil, and then replacing it with one of these other values, consider just sending the appropriate conversion message instead.

nil.to_i                        # => 0
nil.to_f                        # => 0.0
nil.to_s                        # => ""
nil.to_a                        # => []
nil.to_h                        # => {}

Happy hacking!

Responses