Working with hashes in Ruby

Short answer

Access with hash[:key] or fetch for a required key. Iterate with each taking two block parameters. Hash.new(0) gives you a counting hash for free.

ruby
lessons = { python: 4, php: 5, ruby: 1 }

lessons[:python]              # 4
lessons[:missing]             # nil
lessons.fetch(:missing)       # KeyError — good when it must exist
lessons.fetch(:missing, 0)    # 0

#Iterating

ruby
lessons.each { |track, count| puts "#{track}: #{count}" }
lessons.each_key   { |k| puts k }
lessons.each_value { |v| puts v }

Enumerable methods work, with pairs going in and pairs coming out:

ruby
lessons.select { |_, count| count > 2 }     # {python: 4, php: 5}
lessons.reject { |_, count| count > 2 }     # {ruby: 1}
lessons.max_by  { |_, count| count }        # [:php, 5]
lessons.sum     { |_, count| count }        # 10
lessons.sort_by { |_, count| -count }       # array of pairs

The underscore is the convention for a parameter you must accept but do not use.

#Transforming

ruby
lessons.transform_values { |v| v * 2 }
lessons.transform_keys(&:to_s)
lessons.filter_map { |k, v| k if v > 2 }    # [:python, :php]

filter_map combines select and map in one pass — it keeps whatever the block returns, skipping nil and false.

#Default values

ruby
counts = Hash.new(0)
words.each { |w| counts[w] += 1 }

Any missing key reads as 0, so += works without checking first. For a default that is a fresh object each time, use the block form:

ruby
groups = Hash.new { |h, k| h[k] = [] }
groups[:python] << "Intro"

#Merging

ruby
defaults.merge(overrides)                     # right side wins
a.merge(b) { |_, old, new| old + new }         # resolve conflicts yourself