Introduction to Hashes in Ruby, and in Rails

NJ Pearman
EPFL Extension School
10 min readMay 3, 2018

One of the most fundamental and important types in the Ruby programming language is the Hash. It is a simple and flexible way to store structured values together, and Hashes are used all over the place in Rails application code. There’s not much to it, but for a beginner to Ruby and Ruby on Rails, it’s important to learn all of the different ways that a Hash can be used. It’s equally important to be able to recognize when a Hash is being used in existing code, such as the generated code in Rails apps, and beyond. It’s an important step in the road to really understanding how to use Ruby and Ruby on Rails.

Hashes: a key and a value

So what is a Hash? In technical terms, a Hash is a dictionary-like collection of unique keys and their associated values. Hashes are not exclusive to Ruby, and can be found in many other programming languages and are variously referred to as hashtable, hashset, dictionary, or similar.

One way to visualize a Hash is as a virtual collection of boxes. Each box has a name, which is the the key. And each box can hold one thing or value, which can be retrieved using the key for that box.

Each box has to have a key, but a box can be empty. We can keep adding new boxes to a particular Hash, so a Hash can contain as many unique keys with values as we need. The implementation for a Hash-like structure is fundamental computer science and we won’t be going into those concepts and theories here. Let’s just look at using Hashes in Ruby code.

Some simple Ruby code with Hashes

In Ruby, a Hash is created using curly braces and can be assigned to a variable just like any other object in Ruby. Let’s compare an empty Hash to an Array. (The code samples below can all be run in IRB (or Pry) to test things out. Output has been commented with # so that it is not executed if copy-pasted.)

my_hash = {} # curly braces for a Hash
my_array = [] # and square braces for an Array

In order to get a value for a key, we use square brackets in the same way as an Array, and we can ask for the value for any key even if the key does not exist in a Hash. And we assign a value to a key using the same syntax as assigning an item to a particular index in an Array.

my_array[0] # the first item in the Array, which is nil
# => nil
my_hash['key'] # the value assigned to the key 'key', which is nil
# => nil
# we can assign a value to an index in the Array
my_array[0] = 'value'
my_array[0]
# => “value”
# and we can assign a value to the Hash and retrieve it, in a similar way
my_hash['key'] = 'value'
my_hash['key']
# => “value”

We can compare the output from puts of the Array and the Hash in IRB. This highlights the difference between the Hash and the Array: each value is clearly associated with a key in the Hash and joined together in the output by =>, whereas values in the Array are based on position.

my_hash['class'] = 'button primary' # add a second key to our Hash
my_array[1] = 'button primary' # and a second value to our Array
puts my_hash
# => {"key"=>"value", "class"=>"button primary"}
puts my_array
# => ["value", "button primary"]

The values in a Hash can be any type of object, in the same way that any object can be added to an Array. So Strings, Integers, and Arrays are all valid values in a Hash and those values can be assigned and retrieved in exactly the same way.

my_hash['a_string'] = 'my stored string'
my_hash['an_integer'] = 2233
my_hash['an_array'] = my_array
puts my_hash
# => {"key"=>"value", "class"=>"button primary", "a_string"=>"my stored string, "an_integer"=>2233, "an_array"=>["value", "button primary"]}
my_hash['an_array']
# => ["value", "button primary"]

So values can be any type; what about the keys? Strings are obviously permissible given the examples above. And just like values, any object can be used as a key. So Strings, Integers, Symbols and even Arrays are valid as keys in a Hash.

my_hash['to'] = "home#index"
my_hash[34] = "an integer key"
my_hash[:method] = “GET”
my_hash[my_array] = 'Arrays are a weird key type...'
my_hash[my_array]
# => "Arrays are a weird key type..."

Technically, we can use an Array or other complex object as a key in a Hash if we really wanted to, but that is a very bizarre use case. Integers, Strings and Symbols are the most commonly used types of key in a Hash and of these three types, Strings and Symbols are what we encounter the majority of the time.

When we create a Hash, it’s possible to initialize it some ke-value pairs. The syntax for doing this is identical to the IRB output from puts my_hash.

new_hash = { 'method' => 'GET', 
'class' => 'form',
:action => '/ideas/index' }
puts new_hash
# => {“method”=>“GET”, “class”=>”form”, :action=>”/ideas/index”}
puts new_hash[‘method’]
# => “GET”

The equals followed by a greater than symbol is called a hash rocket. The hash rocket is used to neatly output the Hash from puts but is also the syntax used to associate the key on the left with the value on the right. The values in a Hash that has been initialized in this way is exactly the same as above.

That’s the fundamentals of creating Hashes, and storing and accessing values. The Hash class has plenty of useful methods that make it convenient to manipulate any given Hash. As such, Hashes are used a lot in the Rails framework, as they are a convenient way to collect some related values or objects together, and allow each object to have a key or label.

Using Symbols with Hashes

Symbols are particularly useful as keys within a Hash. They allow us to use simplified syntax when defining Hashes and this particular syntax is what can be seen in many of the files in a Rails application.

Let’s quickly recap the basics of Ruby Symbols. Symbols are a type in Ruby that look similar to Strings, and are declared like this:

:ruby
=> :ruby
my_symbol = :ruby_on_rails
=> :ruby_on_rails

One of the examples above used a Symbol as a key for a Hash, but here is another example of a Hash being initialized with two keys that are Symbols.

hash = { :ruby => 'A programming language', 
:ruby_on_rails => 'A web framework' }
=> {:ruby => "A programming language", :ruby_on_rails => "A web framework"}

Both Strings and Symbols are useful as keys in a Hash because they look like a label. They help describe the purpose of the key that they are being used for. For example, if we want to store a name and location in a Hash, we can use the Strings 'name' and 'location' to store the values and then look up the values.

my_hash = { 'name' => 'Peter Pan', 'location' => 'Neverland' }
=> {"name"=>"Peter Pan", "location"=>"Neverland"}
my_hash['name']
=> "Peter Pan"
my_hash['location']
=> "Neverland"

We expect the value associated with 'name' to be the name of something, and the value associated with 'location' to be the location of something, which is a handy way to use Hashes.

This is exactly the same with Symbols. They are useful as keys because they look like labels.

They are preferrable to Strings, however, because they are more efficient. Creating any object in Ruby requires the use of memory and Strings use more memory than Symbols. Also, everytime we ask for the value associated with a particular key, Ruby needs to process that lookup within the Hash, and Symbols are more efficient for this than Strings as well. So Symbols are preferrable because they are more efficient to process and a more efficient use of memory.

In general, we can say that Symbols are more appropriate to use as keys in a Hash than Strings, although we will sometimes have situations where Strings are more appropriate or useful.

Going back to the previous example of declaring a Hash, we can use Symbols as the keys like this:

my_hash = { :name => 'Peter Pan', :location => 'Neverland' }

Shorthand Hash notation

There is a syntax advantage to using Symbols as keys in Hash as well as the performance benefits. That advantage is a shorthand notation that we can use instead of the “hash rocket” => syntax that links each key and value. This shorthand notation is formatted like this:

my_hash = { name: 'Peter Pan', location: 'Neverland' }

This is exactly equivalent to the previous syntax, but notice how the colon has been inverted to the _end_ of the symbol, e.g. name: rather than the start, :name, and that the hash rocket, =>, is no longer needed.

This is the shorthand notation for declaring a Hash and can only be used when the keys are Symbols. The values can be any type, for example:

parameter_hash = { name: 'Claire', age: 35, level: :expert } 
# the values in this Hash are a String, an Integer, and a Symbol

Recognizing Hashes in Rails code

This shorthand syntax might be familiar to those that have started looking at Ruby on Rails application code, as it is used in various places in Rails applications. Have a look at these next two examples and see how they make use of shorthand Hash syntax. What the examples are doing isn’t as important as the fact that they make use of Hashes.

This is a line from the config/routes.rb file, the Router in a Rails application:

# defines a GET route mapped to the atoms action in the StylesControllerget '/styles/atoms', to: 'styles#atoms'

And this is how to render an ERB partial with parameters within an ERB view, which would be in a view file such as app/views/styles/atoms.html.erb:

<%= render partial: 'button', 
locals: { label: 'Go!', class: 'action' } %>

Both of these are using shorthand Hash syntax to pass a Hash as a parameter. The second example is likely more obvious, because it has explicit curly braces, { … }.

The example from the Router could be rewritten as:

get('/styles/atoms', { to: 'styles#atoms' })

This rewrite is just to make the Hash more obvious. It’s then clearer that the Hash is the second parameter passed to the get( ... ) method.

Because of the flexibility of Ruby’s syntax, this rewrite is equivalent to the original but the original omits the parentheses for the method call and _also_ omits the curly braces for the Hash declaration. Both are perfectly valid and are equivalent.

get '/styles/atoms',   to: 'styles#atoms'
get('/styles/atoms', { to: 'styles#atoms' })

Nesting a Hash within a Hash

Let’s go back to the example that renders the ERB partial, as there is an unanswered question here.

<%= render partial: 'button', 
locals: { label: 'Go!', class: 'action' } %>

There is clearly a Hash containing the keys :label and :class. But there are two other keys, :partial and :locals. This is because this isn’t a simple Hash: it is a nested Hash, or a Hash contained as the value of a key within another Hash.

Let’s have a look as if we were to break down the parameters used in the partial call in IRB. By stepping through and declaring the inner Hash first, then using that Hash as a value in a second Hash the structure is clearer.

inner_hash = { label: 'Go!', class: 'action' }
# => {:label=>"Go!", :class=>"action"}
outer_hash = { partial: 'button', locals: inner_hash }
# => {:partial => 'button', :locals=>{:label=>"Go!", :class=>action"}}

This hash is equivalent to the Hash parameter passed to the partial. In the render call, we’re just collapsing those two lines along with the render method call, and don’t need to include the steps that explicitly assign each Hash to a variable.

That is how to create nested Hashes, and how to recognize a nested Hash that is used as a parameter in code. Let’s look at how to access particular values within a nested Hash. It’s very similar to nesting Arrays.

# it is good coding style to line up the keys when writing multi-line Hashesidea_hash = { title: 'Tour du Mont Blanc',
location: { name: 'Chamonix',
country: 'France' }}
# => {:title=>"Tour du Mont Blanc", :location=>{:name=>"Chamonix", :country=>"France"}}idea_hash[:location]
# => {:name=>"Chamonix", :country=>"France"}
idea_hash[:location][:name]
# => "Chamonix"
idea_hash[:location][:country]
# => "France"

When we have a nested Hash, we can chain the key references together to get to the value that we need. Above, we can see the :location key followed by the :name key to get straight to the value "Chamonix". We can do this no matter how many levels of nesting we have within a Hash. But we have to be careful of encountering an unexpected nil within a nested Hash, so guard clauses that check for nil values are a good idea rather than chaining key lookups together and hoping for the best.

We often use nested Hashes to create a rich structure of values within our Ruby code, but they are not that much more complex to use than a simple Hash as the example above shows.

This has been a brief introduction to the fundamentals of Hashes in Ruby. It is far from everything needed to master the use of Hashes however. There is plenty more to using Hashes effectively, and the Hash class has comprehensive reference documentation on ruby-doc.org.

Summing up

Being able to use Hashes is a fundamental of programming in Ruby. But one of the tricks when starting out with Ruby on Rails, or with other existing Ruby codebases, is the ability to recognize the different ways that Hashes can be declared and used. Hashes are used in all sorts of situations because of their flexibility and simple structure.

Getting a solid understanding of how a Hash can be declared as a parameter in a method call helps to demystify what is going on in the code that we have in front of us. Having read this post, go and have a look through some Ruby code or a Rails application and try to find as many uses of Hashes as possible!

Learn more!

Interested in learning more about Ruby, and Ruby on Rails? I teach Web Application Development at the EPFL Extension School, a certified online learning platform teaching digital skills in data science, web application development, and more. EPFL is one of the world’s leading universities and is ranked the “Number 1 Young University” by the Times Higher Education Ranking.

--

--