Hashes in Ruby
Key‑value maps with flexible key types; symbols are common as keys.
Basics
Create hashes with symbol keys or string keys; symbols are fast, immutable identifiers.
h1 = { name: "Matz", lang: "ruby" }
h2 = { "name" => "Matz", "lang" => "ruby" }
h1[:name] # => "Matz"
h2["lang"] # => "ruby"
Defaults and default procs
Setting a default value returns the same object each time—mutations will be shared. Prefer a block for per‑key defaults.
h = Hash.new([])
h[:a] << 1
h[:b] << 2
h[:a] # => [1, 2] (same default array!)
# Preferred: default block
h = Hash.new { |hash, key| hash[key] = [] }
h[:a] << 1
h[:b] << 2
h[:a] # => [1]
Access: [], fetch, and dig
[] returns nil for missing keys; fetch can supply a default or raise; dig walks nested structures safely.
h = { user: { name: "Ava" } }
h[:missing] # => nil
h.fetch(:missing, 0) # => 0
h.fetch(:missing) { 42 } # => 42 (block default)
h.dig(:user, :name) # => "Ava"
Update and merge
Assign directly or merge hashes; later keys overwrite earlier ones.
h[:lang] = "rb"
updated = h.merge(platform: :unix) # new hash
h.merge!(extra: true) # mutate h
Transform and filter
Change keys/values in bulk or select specific pairs.
h = { a: 1, b: 2, c: 3 }
h.transform_keys! { |k| k.to_s.upcase }
h.transform_values! { |v| v * 10 }
h.select { |k, v| v > 10 } # filtering
h.reject { |k, v| v == 20 }
Iteration and order
Hashes preserve insertion order. Iterate pairs, keys, or values.
h.each { |k, v| puts "#{k}=#{v}" }
h.keys # => ["A", "B", "C"]
h.values # => [10, 20, 30]
Nested hashes and autovivification
Use a default proc to build nested hashes on demand.
nested = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
nested[:users][:by_id][1] = { name: "Ava" }
Key checks and utilities
h.key?(:A)
h.include?("B")
h.has_value?(30)
h.fetch_values("A", "C") # => [10, 30]
Summary
- Prefer symbol keys for identifiers; strings interoperate with external APIs
- Use block defaults to avoid shared‑object pitfalls
- Reach for
fetch,dig, andmerge/merge!for robust access and updates