Tutorial: Hashes in Ruby

Hashes in Ruby are collections of key-value pairs, similar to dictionaries in other programming languages.

They allow you to store and retrieve data efficiently using keys.

What You’ll Learn

1. What Are Hashes?

A hash is a collection of unique keys and their associated values. Keys can be any object (most commonly symbols or strings), and values can be any object.

Example

hash = { name: "Alice", age: 25, city: "New York" }

2. Creating Hashes

2.1 Using Curly Braces

hash = { name: "Bob", age: 30 }
puts hash  # Output: {:name=>"Bob", :age=>30}

2.2 Using the Hash Class

hash = Hash.new
hash[:name] = "Charlie"
puts hash  # Output: {:name=>"Charlie"}

2.3 Default Value

You can specify a default value for missing keys.

hash = Hash.new("Not Found")
puts hash[:missing_key]  # Output: Not Found

3. Accessing Hash Elements

Access Using Keys

person = { name: "Alice", age: 25 }
puts person[:name]  # Output: Alice
puts person[:age]   # Output: 25

Using fetch for Default Values

person = { name: "Alice" }
puts person.fetch(:age, "Unknown")  # Output: Unknown

4. Adding and Updating Elements

Adding New Key-Value Pairs

person = { name: "Alice" }
person[:age] = 25
puts person  # Output: {:name=>"Alice", :age=>25}

Updating Existing Keys

person[:name] = "Bob"
puts person  # Output: {:name=>"Bob", :age=>25}

5. Deleting Elements

Using delete

person = { name: "Alice", age: 25 }
person.delete(:age)
puts person  # Output: {:name=>"Alice"}

6. Iterating Over Hashes

Iterate Through Keys and Values

person = { name: "Alice", age: 25 }
person.each do |key, value|
  puts "#{key}: #{value}"
end
# Output:
# name: Alice
# age: 25

Iterate Through Keys Only

person.each_key { |key| puts key }
# Output:
# name
# age

Iterate Through Values Only

person.each_value { |value| puts value }
# Output:
# Alice
# 25

7. Common Hash Methods

7.1 Check for a Key or Value

person = { name: "Alice", age: 25 }
puts person.key?(:name)     # Output: true
puts person.value?(25)      # Output: true

7.2 Merge Two Hashes

hash1 = { name: "Alice", age: 25 }
hash2 = { city: "New York", age: 30 }
merged = hash1.merge(hash2)
puts merged  # Output: {:name=>"Alice", :age=>30, :city=>"New York"}

7.3 Select Specific Elements

person = { name: "Alice", age: 25, city: "New York" }
adults = person.select { |key, value| key == :age && value >= 18 }
puts adults  # Output: {:age=>25}

7.4 Reject Specific Elements

person = { name: "Alice", age: 25, city: "New York" }
filtered = person.reject { |key, value| key == :city }
puts filtered  # Output: {:name=>"Alice", :age=>25}

7.5 Convert to Array

person = { name: "Alice", age: 25 }
puts person.to_a  # Output: [[:name, "Alice"], [:age, 25]]

7.6 Get Keys or Values

person = { name: "Alice", age: 25 }
puts person.keys   # Output: [:name, :age]
puts person.values # Output: ["Alice", 25]

8. Practical Examples

8.1 Counting Word Frequencies

text = "hello world hello Ruby"
words = text.split
frequencies = Hash.new(0)

words.each { |word| frequencies[word] += 1 }
puts frequencies
# Output: {"hello"=>2, "world"=>1, "Ruby"=>1}

8.2 Grouping by Criteria

people = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 17 },
  { name: "Carol", age: 30 }
]

grouped = people.group_by { |person| person[:age] >= 18 ? :adult : :minor }
puts grouped
# Output: {:adult=>[{:name=>"Alice", :age=>25}, {:name=>"Carol", :age=>30}], :minor=>[{:name=>"Bob", :age=>17}]}

8.3 Using Hash for Default Configurations

def configure(settings = {})
  defaults = { theme: "light", language: "en", notifications: true }
  config = defaults.merge(settings)
  puts config
end

configure(language: "fr", notifications: false)
# Output: {:theme=>"light", :language=>"fr", :notifications=>false}

8.4 Sorting a Hash by Key or Value

person = { name: "Alice", age: 25, city: "New York" }

# Sort by keys
sorted_by_key = person.sort.to_h
puts sorted_by_key  # Output: {:age=>25, :city=>"New York", :name=>"Alice"}

# Sort by values
sorted_by_value = person.sort_by { |key, value| value }.to_h
puts sorted_by_value  # Output: {:name=>"Alice", :city=>"New York", :age=>25}

8.5 Hash with Default Values for Caching

cache = Hash.new { |hash, key| hash[key] = key ** 2 }
puts cache[2]  # Output: 4
puts cache[3]  # Output: 9
puts cache     # Output: {2=>4, 3=>9}

9. Best Practices for Using Hashes

  1. Use Symbols for Keys: Symbols are more memory-efficient and faster than strings as keys.
  2. Use Default Values: Set default values to handle missing keys gracefully.
  3. Merge Hashes for Configurations: Use merge to combine default and custom configurations.
  4. Leverage Iterators: Use each, select, and reject for concise and readable hash operations.

10. Summary

Key Concepts

  • Hashes store key-value pairs.
  • Access values using keys or methods like fetch.
  • Use select, reject, and merge to manipulate hash data.
  • Iterating over hashes is straightforward with each, each_key, and each_value.

Examples in Context

Hashes are versatile and powerful for organizing data in Ruby.

Related posts

Ruby Regular Expressions Tutorial (with Code Examples)

Ruby Exceptions Tutorial (with Code Examples)

Ruby Iterators Tutorial (with Code Examples)