A Hash
in Ruby is a data structure that holds values in the key => value fashion. Symbols are often being used as keys to speed up the process of key lookup.
The Hash
class provides many useful methods, some of them come from the Enumerable
module. We won’t go through all of them now, we’ll rather focus on the often forgotten, but worth its value method: default()
.
Let’s start with the basics. Typical session with hash looks like this:
people = Hash.new
people["Mary"] = 78
people["John"] = 21
people["Frank"] = 52
puts "Name: John, age: #{people["John"]}."
The result is:
Name: John, age: 21.
We put some information concerning people’s names and age and then we display some of them. Given key (“John”) has been found and everything went smoothly. Let’s try to lookup for a non existing key:
people = Hash.new
people["Mary"] = 78
people["John"] = 21
people["Frank"] = 52
puts "Name: George, age: #{people["George"]}."
puts people["George"].inspect
The result now is:
Name: George, age: .
nil
The hash did precisely what we expected: it found nothing under the “George” key. Furthermore, it returned the nil
value. What if we wanted to receive something different in such a situation? That’s where the default()
method comes in.
standard_hash = Hash.new
puts standard_hash.default
our_hash = Hash.new
our_hash.default = 185
puts our_hash.default
The result should be:
nil
185
It seems we can set the default value returned by hash in case the key was not found. Let’s try this out with people and their age example:
people = Hash.new
people.default = 'unknown'
people["Mary"] = 78
people["John"] = 21
people["Frank"] = 52
puts "Name: John, age: #{people["John"]}."
puts "Name: George, age: #{people["George"]}."
Voila! Now our program works like if we expect the unexpected, and our users won’t get concerned if they ask for a person we don’t know anything about. By the way, instead of using the default()
method, we can pass the default value to the Hash
constructor:
people = Hash.new('unknown')
puts people["somebody"]
Then the “unknown” string should appear.
We should take into account that the default()
method can be used in a more sophisticated way, e.g. to introduce the Null Object pattern in our program.
We often work with objects that are not being created locally. Instead, they are returned as a result of another method’s invocation. Sometimes the objects we receive are simply nil
references. We have to check then if object really exists before invoking any of its methods, to avoid the NoMethodError
being raised. Null Object pattern sets us free from having to check if received object is nil every time. Consider the following example:
class Introduce
def initialize(name = '')
@name = name
end
def introduce
puts "Hello, my name is #{@name}!"
end
end
class NullIntroduce < Introduce
def introduce
end
end
strangers = Hash.new
strangers.default = NullIntroduce.new
strangers["Holmes"] = Introduce.new('Sherlock Holmes')
strangers["Santa"] = Introduce.new('Santa Claus')
strangers["Holmes"].introduce
strangers["bvcxertg"].introduce
strangers["Santa"].introduce
We defined the Introduce
class, which can be used to introduce strangers. We also created the NullIntroduce
class, which does absolutely nothing when its object is asked to introduce itself. Then we put some data into the strangers
hash and looked up for the existing and non existing key. The result is following:
Hello, my name is Sherlock Holmes!
Hello, my name is Santa Claus!
When we asked the “bvcxertg” person to introduce itself, nothing happened and this is how it was meant to be. In this simple way, we combined the Ruby hash default()
method with the Null Object design pattern.
It seems that we exhausted the topic. Don’t forget about the default()
method and use it wisely!