Skip to content

Instantly share code, notes, and snippets.

@ryansobol
Last active April 7, 2022 15:02
Show Gist options
  • Save ryansobol/3c1d6104c89c5e47d3b3 to your computer and use it in GitHub Desktop.
Save ryansobol/3c1d6104c89c5e47d3b3 to your computer and use it in GitHub Desktop.
Hashes in Ruby

What's a Hash and why is it important?

A Hash is a collection of key-value pairs. To add, fetch, modify, and delete a value from a Hash, you refer to it with a unique key.

While an Array is indexed by Integers only, a Hash is keyed by any object -- Strings, Integers, etc.

In other programming languages, a Hash might be known as an 'associative array', 'dictionary', or 'HashMap'.

What does a Hash look like?

The empty Hash is just two curly braces:

{}

To create a Hash with a single key-value pair, place the pair inside the curly braces, separated by a hash-rocket =>:

{ 'name' => 'john' }

In this example, 'name' is the key and 'john' is the corresponding value.

NOTE: One space after the opening curly brace {, before the closing curly brace }, and around the hash-rocket => is considered good style.

Multiple key-value pairs are further separated by a comma and a space:

{ 'name' => 'john', 'age' => 40 }

In this example, 'name' and 'age' are the keys. And 'john' and 40 are their corresponding values.

When calling puts on a Hash:

puts 'name' => 'john', 'age' => 40

NOTE: Ruby will fail with a SyntaxError if you use curly braces {}.

It displays the following:

{"name"=>"john", "age"=>40}

NOTE: Ruby always uses double quotation marks when displaying Strings.

Similarly, when calling p on this Hash:

p 'name' => 'john', 'age' => 40

NOTE: Again, Ruby will fail with a SyntaxError if you use curly braces {}.

It also displays the following:

{"name"=>"john", "age"=>40}

How does a Hash compare to an Array?

A Hash and an Array have many commonalities. For example, an Array can be assigned to a variable:

person = ['john', 40]

Similarly, a Hash can be assigned to a variable too:

person = { 'name' => 'john', 'age' => 40 }

An Array can receive a variety of methods:

person = ['john', 40]
person.size  #=> 2

In this example, an Array with two elements is assigned to the person variable.

Similarly, a Hash can receive a variety of methods too:

person = { 'name' => 'john', 'age' => 40 }
person.size  #=> 2

In this example, a Hash with two key-value pairs is assigned to the person variable.

Fetching from a container

A single element of an Array can be fetched by it's index:

person = ['john', 40]
person[0]  #=> 'john'

Likewise, a single value of a Hash can be fetched by it's key:

person = { 'name' => 'john', 'age' => 40 }
person['name']  #=> 'john'

Fetching an element from an Array with a non-existent index returns nil:

person = ['john', 40]
person[2]  #=> nil

Likewise, fetching a value from a Hash with a non-existent key also returns nil:

person = { 'name' => 'john', 'age' => 40 }
person['location']  #=> nil

Modifying a container

Given an Array index, it's element can be reassigned:

person = ['john', 40]
person[0] = 'paul'

Likewise, given a Hash key, it's value can be reassigned:

person = { 'name' => 'john', 'age' => 40 }
person['name'] = 'paul'

Deleting from a container

Given an Array index, it's element can be deleted from the Array:

person = ['john', 40]
person.delete_at(0)  #=> 'john'
person  #=> [40]

NOTE: The element 40 is now at index 0.

Likewise, given a Hash key, it's key-value pair can be deleted:

person = { 'name' => 'john', 'age' => 40 }
person.delete('name')  #=> 'john'
person  #=> { 'age' => 40 }

NOTE: The value 40 is still at key 'age'.

Iterating over a container

Each element and index combination of an Array can be iterated over:

person = ['john', 40]

person.each_with_index do |element, index|
  puts "#{element} at #{index}"
end

Which displays the following:

john at 0
40 at 1

Similarly, each key-value pair of a Hash can be iterated over:

person = { 'name' => 'john', 'age' => 40 }

person.each do |key, value|
  puts "#{key}: #{value}"
end

Which displays the following:

name: john
age: 40

How does a Hash contrast to an Array?

Even with some minor quirks, Hashes and Arrays are rather similar. However, there are a few major differences.

Index Type vs Key Type

An Array is indexed by Integers only:

person = ['john', 40]
person[0]  #=> 'john'
person[1]  #=> 40

While a Hash is keyed by any object. For example, by Strings:

person = { 'name' => 'john', 'age' => 40 }
person['name']  #=> 'john'
person['age']   #=> 40

Or by Integers:

person = { 1 => 'john', 2 => 40 }
person[1]  #=> 'john'
person[2]  #=> 40

Or by a mixture of both Strings and Integers:

person = { 'name' => 'john', 2 => 40 }
person['name']  #=> 'john'
person[2]       #=> 40

Though it's rarely done, a Hash can even be keyed by an Array or a Hash.

NOTE: It's extremely common for a Hash to be keyed by a Symbol -- another object type you'll see shortly.

Index Order vs Value Order

An Array is ordered by the numerical value of each index, which always starts at 0:

person = ['john', 40]

person.each_with_index do |element, index|
  puts "#{element} at #{index}"
end

Which displays the following:

john at 0
40 at 1

On the other hand, a Hash is ordered by the insertion order of each the key-value pair:

person = { 'name' => 'john', 'age' => 40 }

person.each do |key, value|
  puts "#{key}: #{value}"
end

Which displays the following:

name: john
age: 40

Adding an Element vs Adding a Key-Value Pair

There are a variety of ways to add an element to an Array.

It can be pushed to the end of an Array by using either the push method or shovel << operator:

person = ['john', 40]
person.push('paul')  #=> ['john', 40, 'paul']
person << 72         #=> ['john', 40, 'paul', 72]

It can be unshifted to the beginning of an Array by using the unshift method:

person = ['john', 40]
person.unshift('paul')  #=> ['paul', 'john', 40]

And it can be inserted at a specific index of the Array with the insert method:

person = ['john', 40]
person.insert(1, 'paul')  #=> ['john', 'paul', 40]

All of these techniques make it easy for you to manage the order of the elements in an Array.

On the other hand, there is only one way to add a key-value pair to a Hash. And that is with the assignment = operator:

person = { 'name' => 'john', 'age' => 40 }
person['friend'] = 'paul'
person  #=> { 'name' => 'john', 'age' => 40, 'friend' => 'paul' }

In this example, the value 'paul' was assigned to the 'friend' key. Since the 'friend' key didn't exist in the Hash, the key-value pair was then added to the end.

Concatenation Arrays vs Merging Hashes

Two Arrays can be concatenated together, forming a new Array containing all the elements of both Arrays:

person = ['john'] + [40]
person  #=> ['john', 40]

However, two Hashes cannot be concatenated together:

person = { 'name' => 'john' } + { 'age' => 40 }
# NoMethodError: undefined method `+' for {"name"=>"john"}:Hash

Instead, two Hashes can be merged together:

person = { 'name' => 'john' }.merge('age' => 40)
person  #=> { 'name' => 'john', 'age' => 40 }

NOTE: A Hash used as a method argument does not need the curly braces {}.

At first glace, merging looks just like concatenation. However, when both Hashes have the same key, the Hash inside the merge method takes precedence:

person = { 'name' => 'john', 'age' => 40 }.merge('age' => 35)
person  #=> { 'name' => 'john', 'age' => 35 }

In this example, both Hashes contain the 'age' key. When merged together, the 'age' => 35 key-value pair overwrites the 'age' => 40 key-value pair. In other words, key-value pairs from the Hash inside the merge method overwrite key-value pairs from the Hash receiving the method call.

@ckotsovos
Copy link

Finally instructions with clear examples. Great Job!

@abcheromar
Copy link

What confusion i have had--its all gone now!!! ):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment