Introducing Hashes

Learning Goals

  • Understand that there are multiple types of collections
  • Develop a mental model to understand hashes
  • Gain some familiarity with common hash methods

Vocabulary

  • Hash
  • Key
  • Value
  • Symbol
  • Accessing Values
  • Assigning Values

Slides

Available here

WarmUp

Ruby Doc defines a hash as “a dictionary-like collection of unique keys and their values. Also called associative arrays, they are similar to Arrays, but where an Array uses integers as its index, a Hash allows you to use any object type.”

  • What information can you tease from this definition?

Intro - Hash Properties

Hashes are the second most important data structure in Ruby. Like an Array, a Hash is a data structure used for representing a collection of things. But whereas an Array generally represents a list of things (ordered, identified by numeric position), we use a Hash to represent a collection of named values. These names are often called keys or attributes. In a Hash, we can insert data by assigning it to a name and later retrieving it using the same name.

Some languages call their Hashes dictionaries for this reason – you look up a word (the label) to retrieve its definition (the data or value with which the label was associated).

Key ideas:

  • Ordered vs. Unordered
  • Pairs
  • Determinism and uniqueness
  • Choosing a hash vs an array
  • Performance characteristics

Working with a Hash

Hashes boil down simply to a collection of key/value pairs.

Keys must be unique.

Values can be any data type (including arrays and hashes).

Creating a Hash

new_hash = {}

or

new_hash = Hash.new

When using the Hash.new, syntax, we’re able to pass a default hash value in as a parameter to new.

new_hash = Hash.new(0)

In the above declaration, the default value of any key created for new_hash has a default value of 0. Keep this in mind for the future - you may find it helpful down the road.

Hash Keys

Let’s imagine needing to store the specifications of different television models as a hash. Each individual tv’s specifications, or values, will be unique to it, but all televisions share the same attributes.

Let’s imagine all of our tv hashes will store information for their screen_size, price, and brand.

A simple example of a television modeled as a hash:

new_tv = {
  "screen_size" => 50,
  "price" => 300,
  "brand" => "Samsung"
}

Take a note of the syntax there. You’ll likely come across it down the road.

How does that differ from the following syntax?

new_tv = {
  screen_size: 50,
  price: 300,
  brand: "Samsung"
}

There are a variety of ways to structure your hash’s syntax, but the above is the most preferred.

Rather than using strings as keys, we’re using symbols.

A symbol, on its own, looks like this: :symbol.

In Ruby, strings are compared character by character, but symbols are compared by their object_id. Thus, symbols help your code run faster.

Accessing Hash Values

What did we use to access the values in an array?

The information contained within a hash is unordered, so we cannot rely on the value’s position to access it.

While we don’t have indexes, we do have keys!

.keys

Ah, .keys. Our first hash method.

Within our same pry session, let’s run new_tv.keys. What do we get? What data type is this returned value?

.values

Just like we used .keys, let’s try out .values. Wow, what useful information!

Getting by Key: []

In theory, if we know the keys we’ve set in our hash, accessing their values is quite simple.

Let’s access the screen_size of our new_tv.

What happens if we access screen_size as a string instead of a symbol?

What happens if we retrieve a value from a key that does not exist?

Setting by Key: []=

Let’s change our new_tv’s screen_size to 60.

Let’s add a new attribute to our tv, resolution and set that equal to “720p”.

Pair Exercise

For this exercise you’ll work in pairs.

  • Person A is in charge of reading the instructions
  • Person B is in charge of working in pry (in such a way that their partner can see!)

Steps

  1. Create a hash called new_band.
  2. Add a bassist to your new_band hash.
  3. Find the name of your bassist by accessing the :bassist key in the new_band hash.
  4. Find the value attached to :vocalist in your hash.
  5. Add a vocalist to your hash.
  6. Add a drummer to your hash.
  7. What are the keys of your hash? What kind of object does that method return?
  8. What are the values of your hash? What kind of object does that method return?
  9. Assign a new value to the :vocalist key of your hash.
  10. How has keys changed after the last step? How has values changed? What

Independent Work

Finally let’s break up for some independent work with Hashes and Arrays.

Hash and Array Nesting

As our programs get more complex, we’ll sometimes encounter more sophisticated combinations of these structures. Consider the following scenarios:

Array within an Array

a = [[1, 2, 3], [4, 5, 6]]
  • what is a.count?
  • what is a.first.count?
  • how can I access the element 5?

Hash within an Array

a = [{pizza: "tasty"}, {calzone: "also tasty"}]
  • what is a.count
  • what is a.first.count
  • how can I access the value "also tasty"

Hash within a Hash

h = {
  dog: {
    name: "Chance",
    weight: "45 pounds"
  },
  cat: {
    name: "Sassy",
    weight: "15 pounds"
  }
}
  • what is h.count?
  • what is h.keys?
  • what is h.values?
  • how can I access the valued "15 pounds"?

Wrap Up

  • Create a Venn Diagram of Arrays & Hashes. Think about how they’re structured, when you would use each one, and nuances to how you interact with each one.

Further Practice

Ruby Docs

Get familiar with the Ruby Docs on Hashes

Practicing with Hashes and Nesting

Now that we’ve worked through the basics, complete Challenge 2 from the Collections Challenges

From the Top

Now you’ve got a decent understanding of hashes. Let’s go at it from the beginning and try to fill a few of the gaps: work through the Hashes section of Ruby in 100 Minutes to pickup a bit more.