Axel Briche
Axel Briche

Reputation: 727

How to simplify creating a deeply nested hash in Ruby?

I currently have:

@teams ||= {}
@teams[date] ||= {}
@teams[date][team] ||= {}
@teams[date][team][raw_address] ||= {}
@teams[date][team][raw_address]['address'] = address
@teams[date][team][raw_address]['orders'] ||= {}
@teams[date][team][raw_address]['orders'][order.id] = order

Is it possible to "remove" the lines containing ||= {}? I just want to have something like:

@teams[date][team][raw_address]['address'] = address
@teams[date][team][raw_address]['orders'][order.id] = order

Upvotes: 1

Views: 67

Answers (1)

Stefan
Stefan

Reputation: 114178

You might know that in Ruby, hashes can have default values.

So when initially creating the @team hash, you could set a default value of e.g. empty hash:

@team = Hash.new { |h, k| h[k] = Hash.new }

Which gives you:

@team[:foo] #=> {}

And the ability to set values in those dynamically created hashes, e.g.:

@team[:foo][:bar] = 123
@team[:foo][:baz] = 456

@team
#=> {
#     :foo => {
#       :bar => 123,
#       :baz => 456
#     }
#   }

But that only works for the first level. To get a hash that can be nested indefinitely, you have to pass along the outer hash's default_proc to the inner hash:

@team = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }

Doing so allows you to create deeply nested hashes by just assigning the last element, e.g.:

@team[:foo][:bar][:baz] = 123
@team[:foo][:bar][:qux][:quux] = 456

@team
#=> {
#     :foo=> {
#       :bar => {
#         :baz => 123,
#         :qux => {
#           :quux => 456
#         }
#       }
#     }
#   }

Upvotes: 5

Related Questions