Exercise 39: Hashes, Oh Lovely Hashes

You are now going to learn about the Hashmap data structure in Ruby. A Hashmap (or "hash") is a way to store data just like a list, but instead of using only numbers to get the data, you can use almost anything. This lets you treat a hash like it's a database for storing and organizing data.

Let's compare what can hashes can do to what arrays lists can do. You see, an array lets you do this:

?> things = ['a', 'b', 'c', 'd']
=> ["a", "b", "c", "d"]
>> puts things[1]
b
=> nil
>> things[1] = 'z'
=> "z"
>> puts things[1]
z
=> nil
>> things
=> ["a", "z", "c", "d"]

You can use numbers to "index" into a array, meaning you can use numbers to find out what's in arrays. You should know this about arrays by now, but make sure you understand that you can only use numbers to get items out of a array.

What a Hash does is let you use anything, not just numbers as your index. Yes, a Hash associates one thing to another, no matter what it is. Take a look:

?> stuff = {'name' => 'Zed', 'age' => 39, 'height' => 6 * 12 + 2}
=> {"name"=>"Zed", "age"=>39, "height"=>74}
>> puts stuff['name']
Zed
=> nil
>> puts stuff['age']
39
=> nil
>> puts stuff['height']
74
=> nil
>> stuff['city'] = "San Francisco"
=> "San Francisco"
>> print stuff['city']
San Francisco=> nil

You will see that instead of just numbers we're using strings to say what we want from the stuff hash. We can also put new things into the hash with strings. You aren't limited to strings for keys or values. We can also do this:

?> stuff[1] = "Wow"
=> "Wow"
>> stuff[2] = "Neato"
=> "Neato"
>> puts stuff[1]
Wow
=> nil
>> puts stuff[2]
Neato
=> nil
>> stuff
=> {"name"=>"Zed", "age"=>39, "height"=>74, "city"=>"San Francisco", 1=>"Wow", 2=>"Neato"}

In this code I used numbers, and you can see numbers and strings as keys in the hash when I print it. I could use anything. Well, almost but just pretend you can use anything for now.

Of course, a hash that you can only put things in is pretty stupid, so here's how you delete things, with the delete function:

?> stuff.delete('city')
=> "San Francisco"
>> stuff.delete(1)
=> "Wow"
>> stuff.delete(2)
=> "Neato"
>> stuff
=> {"name"=>"Zed", "age"=>39, "height"=>74}

A Hash Example

We'll now do an exercise that you must study very carefully. I want you to type this code in and try to understand what's going on. Take note of when you put things in a hash, get from a hash, and all the operations you use. Notice how this example is mapping states to their abbreviations, and then the abbreviations to cities in the states. Remember, "mapping" or "associating" is the key concept in a hash.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# create a mapping of state to abbreviation
states = {
  'Oregon' => 'OR',
  'Florida' => 'FL',
  'California' => 'CA',
  'New York' => 'NY',
  'Michigan' => 'MI'
}

# create a basic set of states and some cities in them
cities = {
  'CA' => 'San Francisco',
  'MI' => 'Detroit',
  'FL' => 'Jacksonville'
}

# add some more cities
cities['NY'] = 'New York'
cities['OR'] = 'Portland'

# puts out some cities
puts '-' * 10
puts "NY State has: #{cities['NY']}"
puts "OR State has: #{cities['OR']}"

# puts some states
puts '-' * 10
puts "Michigan's abbreviation is: #{states['Michigan']}"
puts "Florida's abbreviation is: #{states['Florida']}"

# do it by using the state then cities dict
puts '-' * 10
puts "Michigan has: #{cities[states['Michigan']]}"
puts "Florida has: #{cities[states['Florida']]}"

# puts every state abbreviation
puts '-' * 10
states.each do |state, abbrev|
  puts "#{state} is abbreviated #{abbrev}"
end

# puts every city in state
puts '-' * 10
cities.each do |abbrev, city|
  puts "#{abbrev} has the city #{city}"
end

# now do both at the same time
puts '-' * 10
states.each do |state, abbrev|
  city = cities[abbrev]
  puts "#{state} is abbreviated #{abbrev} and has city #{city}"
end

puts '-' * 10
# by default ruby says "nil" when something isn't in there
state = states['Texas']

if !state
  puts "Sorry, no Texas."
end

# default values using ||= with the nil result
city = cities['TX']
city ||= 'Does Not Exist'
puts "The city for the state 'TX' is: #{city}"

What You Should See

$ ruby ex39.rb
----------
NY State has: New York
OR State has: Portland
----------
Michigan's abbreviation is: MI
Florida's abbreviation is: FL
----------
Michigan has: Detroit
Florida has: Jacksonville
----------
Oregon is abbreviated OR
Florida is abbreviated FL
California is abbreviated CA
New York is abbreviated NY
Michigan is abbreviated MI
----------
CA has the city San Francisco
MI has the city Detroit
FL has the city Jacksonville
NY has the city New York
OR has the city Portland
----------
Oregon is abbreviated OR and has city Portland
Florida is abbreviated FL and has city Jacksonville
California is abbreviated CA and has city San Francisco
New York is abbreviated NY and has city New York
Michigan is abbreviated MI and has city Detroit
----------
Sorry, no Texas.
The city for the state 'TX' is: Does Not Exist

What Hashes Can Do

Hashes are another example of a data structure, and like arrays they are one of the most commonly used data structures in programming. A hash is used to map or associate things you want to store to keys you need to get them. Again, programmers don't use a term like "hash" for something that doesn't work like an actual hash full of words, so let's use that as our real world example.

Let's say you want to find out what the word "Honorificabilitudinitatibus" means. Today you would simply copy-paste that word into a search engine and then find out the answer, and we could say a search engine is like a really huge super complex version of the Oxford English Dictionary (OED). Before search engines what you would do is this:

  1. Go to your library and get "the dictionary". Let's say it's the OED.
  2. You know "honorificabilitudinitatibus" starts with the letter 'H' so you look on the side of the book for the little tab that has 'H' on it.
  3. Then you'd skim the pages until you are close to where "hon" started.
  4. Then you'd skim a few more pages until you found "honorificabilitudinitatibus" or hit the beginning of the "hp" words and realize this word isn't in the OED.
  5. Once you found the entry, you'd read the definition to figure out what it means.

This process is nearly exactly the way a hash works, and you are basically "mapping" the word "honorificabilitudinitatibus" to its definition. A hash in Ruby is just like a dictionary in the real world like the OED.

Study Drills

  1. Do this same kind of mapping with cities and states/regions in your country or some other country.
  2. Find the Ruby documentation for hashes and try to do even more things to them.
  3. Find out what you can't do with hashes. A big one is that they do not have order, so try playing with that.

Common Student Questions

What is the difference between an array and a hash?
A array is for an ordered array of items. A hash (or hash) is for matching some items (called "keys") to other items (called "values").
What would I use a hash for?
When you have to take one value and "look up" another value. In fact you could call hashes "look up tables."
What would I use an array for?
Use an array for any sequence of things that need to be in order, and you only need to look them up by a numeric index.

Buy DRM-Free

When you buy directly from the author, Zed A. Shaw, you'll get a professional quality PDF and hours of HD Video, all DRM-free and yours to download and use as you see fit.

$29.99

Buy Directly From The Author

Or, you can read Learn Ruby the Hard Way for free right here, video lectures not included.