Ruby Enumerables: Hashes

The most famous framework based on Ruby language, Ruby on Rails, makes extensive use of hash. And cause in our blog we talk mainly about development with Ruby on Rails it seem fair to me to explore some language constructs that are widely used by the Rails framework. In this and in the next posts I will briefly present characteristics and properties of some of the most widely used constructs, starting from Hash. Hashes, also known as associative arrays or dictionaries are very flexible data structures that allow you to assign a value to a key that is not necessarily an integer. This is the first difference with arrays. Another feature that distinguishes hash from array is that a hash is typically an unordered structure. 1. Creation and initializing Create an hash is very easily using the method [] . Of course, an hash can also be initialized with some values during it creation. Some examples:

a = Hash.[]   # Create a new empty hash 
# Create a new initialized hash 
b = Hash.[]("name", "Claudio", "age", "30")
c = Hash.[]("name" => "Claudio", "age" => "30")
d = Hash["name", "Claudio", "age", "30"]
e = Hash["name" => "Claudio", "age" => "30"]
f = {"name", "Claudio", "age", "30"}
g = {"name" => "Claudio", "age" => "30"}
All the hashes from a to g will be populated in the same way. I Personally prefer the syntax used in the last line, but the result is absolutely identical for each of the syntax showed. You can 'also initialize an hash with a default value that will be returned instead of nil if you try to access a nonexistent key. To do this you can use the method new.

a = Hash.new   # Create a new empty hash 
   # Create a new hash with default value = -1
b = Hash.new(-1)
b["test"]   # -1
b.inspect   # {}
The default value can also be set after creating the hash using the default= method.

a = Hash.new   # Create a new empty hash 
a["test"]   # nil
a.default= "no value"
a["test"]   # "no value"
a.default   # "no value"
There is also a fetch method that returns an IndexError exception if you try to read a nonexistent key. The fetch method can be invoked with a second parameter that acts as a default value or with a block of code that generates the default value at runtime if the key doesn't exist.

a = {"name" => "Claudio", "age" => 30, "city" => "Verona"}
a.fetch("surname")                      # IndexError
a.fetch("surname", "Marai")          # "Marai"
a.fetch("age", "Marai")                 # 30
a.fetch("name"){|x| x.upcase}      # "Claudio"
a.fetch("surname"){|x| x.upcase}  # "SURNAME"
2. Insert, edit, delete To add new values, access or change existing values, hash provides methods [] and []= that can be used as follows:

a = {"name" => "Claudio"}
a["name"]                  # "Claudio"
a["age"]= 30               
a["age"]                   # 30
a["age"]= 31               
a["age"]                   # 31
You can remove a specific value from the hash by using the delete method passing the key of key-value pair to be removed as parameter

a = {"name" => "Claudio", "age" => 30}
a.delete("name")
a["name"]                  # nil
a["age"]                   # 30
The clear method will instead remove all the hash key-value pairs. The delete_if method lets you delete all the pairs that satisfy a certain condition specified in a block.

a = {"name" => "Claudio", "age" => 30, "city" => "Verona"}
a.delete_if{|key, value| key.include? "c"}
# a => {"name" => "Claudio", "age" => 30}
Finally, the reject method is like using delete_if but on a copy of the hash that is returned by the method itself. Basically invoke a.reject is equivalent to invoke a.dup.delete_if. This concludes this excerpt on the hash's basic operations. In the next post I will describe how to iterate over the values of a hash, convert it to array and other operations such as merge and sort.