Chapter 2: Part 2

Hashes are dictionaries made up of a collection of key-value pairs. Hash syntax looks like this:

legends = {'Smalls' => 'Scotty',
'The Jet' => 'Benny',
'Ham' => 'Hamilton',
'Squints' => 'Michael',
'Yeah-Yeah' => 'Alan',
}


Above we created a hash using literal notation syntax, which is the preferred notation, though Ruby's flexibility accomodates different syntaxes. Each key (e.g. 'Ham') points at a value (e.g. 'Hamilton') using the => operator. We also could have made an empty hash with a pair of curly braces legends = {} and then added to it. Let's add another kid to our team:

legends['Repeat'] = 'Tommy'


This will add the 'Repeat' => 'Tommy' key-value pair to the legends hash. We can access hashes just like arrays except instead of using incidices, we use simply reference the key:

puts legends['Squints']


In this example, Ruby would print Michael. This method works because all keys in a Ruby hash must be unique, though values can repeat as often as we'd like. When we try to access a hash using a bum key:

puts legends['The Beast']


Instead of throwing an error like most languages, in the above case Ruby returns a special value called nil which is sort of a 'non-existence' value.

Just like with arrays, we need to know how to iterate over them to fully unlock their potential. The procedure and syntax is almost the same as with arrays:

fave_lang = {'Keith' => 'Javascript',
'Erik' => 'Python',
'Jimmy' => 'PHP',
'Graeme' => '.NET',
'Scott' => 'Ruby'}

fave_lang.each {|person, language| puts person + ' loves using ' + language}


Notice the two parameters person and language inside our code block. They represent the key-value pairing in the hash. The above examples iterates through the entire hash, printing a concatenated string for each pairing. We can also iterate over just the keys or just the values in a hash using Ruby's .each_key and .each_value methods.

So far we have been using strings as the keys in our hashes. But Ruby hashes work much better using Ruby symbols instead. Symbols are like lightweight strings. They always start with a colon and must be valid Ruby variable names, meaning the first character has to be a lowercase letter or an underscore like in :my_symbol. The rest of the symbol name can contain letters, numbers, and underscores. Here is how we use symbols in the creating of a hash:

my_hash = {javascript: 'clunky',
python: 'simple',
ruby: 'elegant'}


Notice that in the example, the : is on the opposite side of the symbol name versus the previous example. This is partly so that we can eliminate the so called 'hash rocket' => when pairing keys and values. Ruby syntax is always being updated to be simpler and more concise. We can see one of the important differences between symbols and strings with Ruby's built in .object_id method:

puts 'string'.object_id
puts 'string'.object_id

puts :symbol.object_id
puts :symbol.object_id


Running this in RubyMine will show two different object ID numbers for both 'string's (e.g. 70243192635920 and 70243192635840), while showing the same object ID number for both :symbols (e.g. 384488). This is because symbols are not strings. It's not just the syntax that differs. For example, symbols cannot be changed once they have been created. Also, while there can be many strings all with the same value, there's only one copy of any given symbol at one time. Both of these differences result in increased speed and more efficient memory allocation when using symbols as hash keys. Ruby also has a handy .to_sym method for converting strings into symbols.

We can filter through a hash using specific criteria. Using Ruby's .select method, we can make up a code block that returns specific values:

yelp_reviews = {nori: 3.5,
mesob: 4.5,
spice2: 4.0,
bamboo: 3.0,
fresco: 2.0}

best_food = yelp_reviews.select {|name, rating| rating > 3}


This time our code block we added criteria as to what key-value pairs we want to select. In the above example we created a variable best_food and stored restaurants with ratings above 3 stars. Were we to puts best_food, we would get:

{:nori=>3.5, :mesob=>4.5, :spice2=>4.0}