Hash Concept Review
Introduction
Hashes and arrays are similar in many ways.
- They are both data structures
- They are essential tools for programmers to understand
- They are both used to hold collections of things
They differ in how we find elements in the collection and whether the items have any ordering mechanism.
Learning Goals
- Recognize hash as a core tool in programming
- Recognize hash vocabulary word: Key
- Recognize hash vocabulary word: Value
- Compare Weakness of Nested
Array
s versus aHash
- View a
Hash
in Ruby - Access
Hash
Elements Through Bracket-Notation in Ruby - Create
Hash
es in Ruby
Recognize Hash as a Core Tool in Ruby
Hashes are an important and commonly used data type. Ruby, Python, and JavaScript developers use hashes (or something very similar to it) every day. Take your time and make sure you understand how they work.
Think of hashes like a dictionary. Dictionaries have a unique word that points
to a definition of that word. The word ant
points to a definition like "a
small insect that likes to ruin picnics" and anteater
is a unique word that
points to a definition like "a large mammal that likes to eat ants."
Recognize Hash Vocabulary Word: Key
In a dictionary we lookup a definition by the word. When we look things up in a hash we call the unique thing we search by the key. Some documentation calls the key the index โ just like with an array. We're going to call it a key.
Recognize Hash Vocabulary Word: Value
In a dictionary, when we lookup a definition by the word we get its definition. When we look things up in a hash based on the key, we get back the value of that key in the hash.
Unlike a real dictionary which uses words or (String
s) as keys, you can use
Integer
s, Symbol
s, a Boolean
or even an Array
as a key in your hash.
Most of the time we use a String
, though.
Compare Weakness of Nested Arrays versus a Hash
To recognize the value of using a hash, let's take a look at an example array that contains 5 different fruits:
fruits = ["mango", "papaya", "coconut", "plantain", "dragonfruit"]
Let's say that Flatiron Genetic Engineering has now created new colors of these 5 fruits. They sent the new information over to us in the form of a table:
Fruit | Color Varieties |
---|---|
mango | orange,red,green |
papaya | orange,green |
coconut | brown, white |
plantain | green,yellow, brown |
dragonfruit | red, white, black |
Now that we have a list of modified fruits, what if we wanted to associate the color varieties with each fruit? We could try to make an array of arrays or "nested array" work like so:
fruits = [
["mango", ["orange","red","green"]],
["papaya", ["orange","green"]],
["coconut", ["brown", "white"]],
["plantain",["green","yellow", "brown"]],
["dragonfruit",["red", "white", "black"]]
]
But how would we get the varieties of the "plaintain"
? Let's write the
pseudocode
key= "plaintain"
counter = -1
fruits_array_length = calculate_length_of_array(fruits)
For each element in the fruits array
counter = counter + 1
if counter > fruits_array_length
print "Could not find the key"
exit
end
if counter % 2 == 0
if fruits[counter] == key
return fruits[counter + 1]
end
end
We would have to loop through the array by even-number increments from 0, find
the index that matches the string, add 1
to that index, and get back the
array. We'd also need to do something for if the string was not found.
Gross.
What we want to say is "Hey hash! Give me the array associated with the
String
"plantain"
. Or, more technically, "Hey hash, let me give you a
key, please return its corresponding value."
View a Hash
in Ruby
Let's assume someone defined our fruit table as a Hash
in Ruby. They've
assigned it to the constant FRUITS
. We can print the contents in IRB like so
using the p
command:
2.3.3 :001 > p FRUITS
{"mango"=>["orange", "red", "green"], "papaya"=>["orange", "green"], "coconut"=>["brown", "white"], "plantain"=>["green", "yellow", "brown"], "dragonfruit"=>["fuschia", "white", "black"]}
=> {"mango"=>["orange", "red", "green"], "papaya"=>["orange", "green"], "coconut"=>["brown", "white"], "plantain"=>["green", "yellow", "brown"], "dragonfruit"=>["fuschia", "white", "black"]}
As we can see, while Array
s are surrounded by square-brackets ([]
), Hash
es
are surrounded by curly-braces ({}
). The key is listed and is shown
"pointing to" with an arrow (=>
) the value. So we see that "mango"
points
to ["orange", "red", "green"]
.
Access Hash
Elements Through Bracket-Notation in Ruby
To access the contents of a Hash
we provide the key in bracket notation
(just like an Array
).
Thus:
FRUITS["dragonfruit"] #=> ["fuschia", "white", "black"]
FRUITS["dragonfruit"][1] #=> "white"
Create Hash
es in Ruby
Just like Array
, in Ruby, we can create Hash
es with a Hash
-literal format
or with a constructor method called Hash.new
. _Just like with Array_, most of the time, when creating a
Hash` from scratch, we prefer the Hash
literal syntax.
Hash Literal Syntax
Here's your own fruits
hash:
fruits = {
"mango" => ["orange","red","green"],
"papaya" => ["orange","green"],
"coconut" => ["brown", "white"],
"plantain" => ["green","yellow", "brown"],
"dragonfruit" => ["fuschia", "white", "black"]
}
Since we're getting comfortable with nested data structures, let's try nesting
our Hash
es:
fruits = {
"mango" => {
"colors" => ["orange","red","green"],
"nutrients" => ["vitamin A", "vitamin B-6", "vitamin C"]
},
"papaya" => {
"colors" => ["orange","green"],
"nutrients" => ["vitamin C", "magnesium", "potassium"]
}
}
STRETCH: Use bracket notation to print out the first nutrient for a papaya.
Use the new
Keyword
Unlike Array
, it's common to use the Hash.new
constructor method.
groceries = Hash.new # OR: groceries = {}
groceries[1] = "potatoes"
groceries[2] = "onions"
groceries[3] = "eggs"
puts groceries
# => {1=>"potatoes", 2=>"onions", 3=>"eggs"}
The store
keyword can also be used to add values to the new hash in place of
square brackets []
.
groceries = Hash.new
groceries.store(1, "potatoes")
groceries.store(2, "onions")
groceries.store(3, "eggs")
puts groceries
# => {1=>"potatoes", 2=>"onions", 3=>"eggs"}
Quick Check
As a stretch, try this code out in your head, and then in IRB:
directors = {}
directors.store("Coppola", Hash.new)
directors["Coppola"] = { "Francis Ford" => { "movies" => ["The Godfather", "Apocalypse Now"], "life" => "Guy from Detroit tells the triumph and tragedy of a dream called 'America'"} }
directors["Coppola"]["Sophia"] = {}
directors["Coppola"]["Sophia"]["movies"] = ["Lost in Translation", "Virgin Suicides"]
directors["Coppola"]["Sophia"].store("life", "Tough childhood in the Philippines with her father, tried acting, met Kirsten Dunst and made moody movies")
What are the value of these look-ups?
directors["Coppola"]["Francis Ford"]["movies"][2]
directors["Coppola"]["Sophia"]["movies"]["life"]
directors["Coppola"]["Francis Ford"]["movies"][1]
- `directors["Coppola"]["sophia"]["life"] ``
Conclusion
Hashes are a universal programming concept that are great for faster, simpler
data access. While arrays are used for any sequence of things that need to be in
order, hashes are used to map or associate information you want to store to
keys. They can be seen as "look up tables," like a dictionary or glossary.
Hashes can be created by initializing a new hash with the .new
method or an
empty hash with {}
and assigning values to keys with the bracket notation,
.store
, or with the literal =>
. Now we can retrieve different types of data
objects much more easily!