Hashes
Ruby is a dynamic, beginner-friendly programming language, and a Hash stores data as key-value pairs so you can look things up by a meaningful name instead of a position.
Learn Hashes in our free Ruby course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Ruby course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll create hashes, read and update values by key, iterate over pairs, and use defaults for missing keys.
What You'll Learn in This Lesson
1️⃣ Creating, Reading, and Updating
Write a hash with { key: value } . Symbol keys (the name: form) are the norm. Read with hash[:key] , and add or change a value by assigning to a key. An unknown key returns nil rather than erroring.
2️⃣ Iterating and Defaults
Loop over pairs with each and two block parameters. Use fetch when you want a missing key to raise (or supply a default). And Hash.new(0) gives every missing key a starting value — the classic recipe for counting things.
Your turn. Manage a small shop inventory hash. Fill in the TODO lines, then run it.
Mini-Challenge: A Word Counter
Count how many times each word appears in a sentence using Hash.new(0) . Run with ruby wordcount.rb .
Common Errors (and the fix)
- Symbol vs string key mismatch — hash[:name] and hash["name"] are different keys. Be consistent.
- nil from a missing key — Looking up an absent key gives nil , which can cause errors later. Use fetch to fail fast.
- "undefined method for nil" — Often you indexed a missing key, got nil , then called a method on it. Check the key exists.
- Forgetting the second block param — Iterate pairs with each { |k, v| ... } , not just |k| .
- Old hash-rocket syntax confusion — { :a => 1 } and { a: 1 } mean the same thing for symbol keys.
Pro Tips
- 💡 dig for nested hashes: data.dig(:user, :name) safely reaches inside without nil crashes.
- 💡 merge to combine: defaults.merge(overrides) builds settings with sensible fallbacks.
- 💡 transform values: h.transform_values { |v| v * 2 } maps over the values cleanly.
📋 Quick Reference — Hashes
Concept
Ruby Syntax
Create
{ name: "Ada" }
Read / write
h[:name] = "Bo"
Keys / values
h.keys / h.values
Has key?
h.key?(:name)
Safe lookup
h.fetch(:k, 0)
Iterate
h.each { |k, v| }
Frequently Asked Questions
🎉 Lesson Complete!
- ✅ Hashes map keys to values
- ✅ Symbol keys ( name: ) are the convention
- ✅ Read/write with h[:key] ; missing keys return nil
- ✅ Iterate pairs with each { |k, v| }
- ✅ fetch and Hash.new(0) handle missing keys
- ✅ Next lesson: Ranges — express sequences concisely
Practice quiz
What does a Hash store?
- An ordered list indexed by number
- Only strings
- Key-value pairs looked up by key
- A single value
Answer: Key-value pairs looked up by key. A Hash maps keys to values, so you retrieve data by a meaningful key rather than a position.
Given , what does return?
- nil
- An error
- false
- ""
Answer: nil. By default, looking up an unknown key returns nil rather than raising.
What does return?
- {:a=>1, :b=>3}
- {:a=>1}
- {:a=>2}
- {:a=>2, :b=>3}
Answer: {:a=>2, :b=>3}. merge overrides existing keys with the argument's values: a becomes 2 and b is added.
What does give you?
- A frozen hash
- A default value of 0 for missing keys
- An array of zeros
- A KeyError on miss
Answer: A default value of 0 for missing keys. Hash.new(0) returns 0 for any missing key — perfect for counters with .
How does differ from for a missing key?
- fetch raises KeyError (or returns a default you pass)
- No difference
- fetch returns nil
- fetch is slower only
Answer: fetch raises KeyError (or returns a default you pass). hash[:missing] returns nil, but hash.fetch(:missing) raises KeyError; fetch can also take a default.
Why are symbols (:name) the conventional hash keys?
- They allow duplicates
- They sort automatically
- They're immutable and memory-efficient — every :name is the same object
- They are faster to type only
Answer: They're immutable and memory-efficient — every :name is the same object. Symbols are immutable and reused, so :name is always the same object — efficient for fixed keys.
Are and the same key?
- Yes, always
- No — a symbol and a string are different keys
- Only in Ruby 3
- Only for short strings
Answer: No — a symbol and a string are different keys. A symbol key and a string key are distinct; mixing them is a common source of nil surprises.
How do you iterate over key/value pairs of a hash?
- each { |k| }
- map { |v| }
- each_value { |k, v| }
- each { |k, v| }
Answer: each { |k, v| }. Use each with two block parameters: hash.each { |key, value| ... }.
What does return when :name exists?
- the value
- true
- nil
- the key
Answer: true. key? returns true or false depending on whether the key is present.
Since Ruby 1.9, what order does iterating a hash follow?
- Random order
- Sorted by key
- Insertion order
- Reverse order
Answer: Insertion order. Hashes preserve insertion order, so iteration visits pairs in the order they were added.