Ruby Logo

Ruby Hashes

Comprehensive guide to Ruby hashes - key-value pairs, iteration, and hash methods.

Home Ruby Ruby Hashes

Ruby Hashes Mastery

Hashes are Ruby's implementation of associative arrays or dictionaries. They store key-value pairs and provide fast lookup, making them essential for data organization, configuration, and caching.

Hash Basics

Hashes are collections of key-value pairs where keys are unique and can be any object type, though symbols and strings are most common.

# Hash creation methods
# Hash literal with symbols
person = { name: "Alice", age: 30, city: "New York" }
# Hash literal with arrows (rocket syntax)
scores = { "math" => 95, "science" => 87, "english" => 92 }
# Mixed key types
mixed = { :symbol_key => "value1", "string_key" => "value2", 42 => "number_key" }
# Hash.new constructor
empty_hash = Hash.new
default_hash = Hash.new("default_value") # Returns "default_value" for missing keys
# Hash from arrays
keys = [:name, :age, :city]
values = ["Bob", 25, "Chicago"]
from_arrays = Hash[keys.zip(values)]
# Accessing values
puts person[:name] # "Alice"
puts scores["math"] # 95
puts person[:missing] # nil
puts default_hash[:missing] # "default_value"

Hash Operations

Ruby hashes provide comprehensive methods for adding, updating, removing, and querying key-value pairs.

# Adding and updating values
inventory = { apples: 10, bananas: 5 }
inventory[:oranges] = 8 # Add new key-value pair
inventory[:apples] = 15 # Update existing value
inventory.store(:grapes, 12) # Alternative way to add
# Safe access methods
puts inventory.fetch(:apples) # 15
puts inventory.fetch(:pears, 0) # 0 (default value)
puts inventory.dig(:apples) # 15 (safe navigation)
# Checking for keys and values
puts inventory.key?(:apples) # true
puts inventory.has_key?(:pears) # false (alias for key?)
puts inventory.value?(10) # false (10 was updated to 15)
puts inventory.has_value?(15) # true
# Removing elements
removed = inventory.delete(:bananas) # Returns 5 and removes the pair
inventory.delete(:missing) { "Key not found" } # Block executed if key missing
key, value = inventory.delete_if { |k, v| v < 10 } # Remove based on condition
inventory.clear # Remove all elements

Hash Iteration

Ruby provides multiple ways to iterate through hashes, accessing keys, values, or both simultaneously.

# Different iteration methods
grades = { alice: 85, bob: 92, charlie: 78 }
# Iterate over key-value pairs
grades.each do |student, grade|
puts "#{student.capitalize}: #{grade}%"
end
# Iterate over keys only
grades.each_key { |student| puts "Student: #{student}" }
# Iterate over values only
grades.each_value { |grade| puts "Grade: #{grade}%" }
# Iterate with index
grades.each_with_index do |(student, grade), index|
puts "#{index + 1}. #{student}: #{grade}%"
end
# Transform iterations
letter_grades = grades.map { |student, grade| [student, grade >= 90 ? "A" : "B"] }.to_h
passing = grades.select { |student, grade| grade >= 80 }
failing = grades.reject { |student, grade| grade >= 70 }

Essential Hash Methods

# Hash transformation methods
numbers = { a: 1, b: 2, c: 3 }
# Transform values
doubled = numbers.transform_values { |v| v * 2 } # {a: 2, b: 4, c: 6}
numbers.transform_values! { |v| v * 10 } # Mutating version
# Transform keys
strings = { "name" => "Alice", "age" => 30 }
symbols = strings.transform_keys(&:to_sym) # {name: "Alice", age: 30}
# Merging hashes
hash1 = { a: 1, b: 2 }
hash2 = { b: 3, c: 4 }
merged = hash1.merge(hash2) # {a: 1, b: 3, c: 4} (hash2 overwrites)
custom_merge = hash1.merge(hash2) { |key, old, new| old + new } # {a: 1, b: 5, c: 4}
# Hash filtering
all_grades = { alice: 85, bob: 92, charlie: 78, diana: 95 }
high_scores = all_grades.filter { |name, grade| grade >= 90 }
low_scores = all_grades.reject { |name, grade| grade >= 80 }
# Hash utilities
puts all_grades.keys # [:alice, :bob, :charlie, :diana]
puts all_grades.values # [85, 92, 78, 95]
puts all_grades.length # 4
puts all_grades.empty? # false
flipped = all_grades.invert # {85 => :alice, 92 => :bob, 78 => :charlie, 95 => :diana}

Hashes as Data Structures

Hashes excel at representing structured data, configuration, and complex nested information.

# Nested hash structures
company = {
name: "TechCorp",
founded: 2010,
headquarters: {
street: "123 Tech Ave",
city: "San Francisco",
state: "CA"
},
employees: [
{ name: "Alice", role: "Developer", salary: 95000 },
{ name: "Bob", role: "Designer", salary: 75000 }
]
}
# Accessing nested data
puts company[:headquarters][:city] # "San Francisco"
puts company.dig(:headquarters, :city) # Safe navigation
puts company.dig(:missing, :key) # nil (doesn't raise error)
# Configuration pattern
config = {
database: {
host: "localhost",
port: 5432,
name: "myapp_production"
},
cache: {
enabled: true,
ttl: 3600
},
features: {
user_registration: true,
email_notifications: false,
beta_features: true
}
}
# Utility method for nested access
def get_config(config, *keys)
config.dig(*keys) || raise("Configuration #{keys.join('.')} not found")
end
puts get_config(config, :database, :host) # "localhost"

Hash Performance & Best Practices

# Performance considerations
# Symbols are faster for keys (immutable, single instance)
fast_hash = { :name => "Alice", :age => 30 }
# Strings create new objects each time (slower)
slower_hash = { "name" => "Alice", "age" => 30 }
# Hash as cache/memoization
class ExpensiveCalculator
def initialize
@cache = {}
end
def fibonacci(n)
return @cache[n] if @cache.key?(n)
result = if n <= 1
n
else
fibonacci(n - 1) + fibonacci(n - 2)
end
@cache[n] = result
end
end
# Hash with default proc for dynamic defaults
word_count = Hash.new { |hash, key| hash[key] = 0 }
"hello world hello".split.each { |word| word_count[word] += 1 }
puts word_count # {"hello"=>2, "world"=>1}
# Hash for grouping
students = [
{ name: "Alice", grade: "A" },
{ name: "Bob", grade: "B" },
{ name: "Charlie", grade: "A" }
]
by_grade = students.group_by { |student| student[:grade] }
puts by_grade["A"].map { |s| s[:name] } # ["Alice", "Charlie"]

Common Hash Patterns

✅ Use Hashes For

  • Configuration data
  • Key-value lookups
  • Caching/memoization
  • Counting occurrences
  • Representing structured data
  • Method parameters (keyword args)

Best Practices

  • Use symbols for static keys
  • Use dig for safe nested access
  • Consider default values with Hash.new
  • Use fetch for required keys
  • Prefer immutable transformations
  • Use meaningful key names

Ruby Hashes Mastery Checklist

  • Creation: Know literal syntax, Hash.new, and conversion methods
  • Access: Use [], fetch, dig for safe access patterns
  • Modification: Add, update, delete with proper methods
  • Iteration: Use each, each_key, each_value as appropriate
  • Transformation: Master map, select, reject, transform_*
  • Merging: Combine hashes with merge and custom conflict resolution
  • Nesting: Handle complex data structures safely
  • Performance: Use symbols for keys and consider caching patterns

Quick Navigation

Related Topics

Video Tutorial

Watch and learn ruby hashes

Pro Tip: After reading through the content above, watch this video to reinforce your understanding and see the concepts in action!