One liner nested hash creation in Ruby? (I come from Perl)
I am a Perl person and I have made Hashes like this for a while:
my %date; #Assume the scalars are called with 'my' earlier $date{$month}{$day}{$hours}{$min}{$sec}++
Now I a开发者_开发技巧m learning Ruby and I have so far found that using this tree is the way to do many keys and a value. Is there any way to use the simple format that I use with Perl using one line?
@date = { month => { day => { hours => { min => { sec => 1 } } } } }
Unfortunately, there is no simple, practical way. A Ruby equivalent would be an ugly, ugly beast like:
((((@date[month] ||= {})[day] ||= {})[hours] ||= {})[min] ||= {})[sec] = 1
There is a way to assign default values for missing keys in hashes, though:
@date = Hash.new { |hash, key| hash[key] = {} }
# @date[:month] is set to a new, empty hash because the key is missing.
@date[:month][:day] = 1
Unfortunately this does not work recursively.
...unless you create it yourself; hooray for Ruby!
class Hash
def self.recursive
new { |hash, key| hash[key] = recursive }
end
end
@date = Hash.recursive
@date[month][day][hours][min][sec] = 1
# @date now equals {month=>{day=>{hours=>{min=>{sec=>1}}}}}
Keep in mind, though, that all unset values are now {}
rather than nil
.
Here's a couple of options similar to the answer given by @molf but without the monkey patch.
Using a factory method:
def hash_tree
Hash.new do |hash, key|
hash[key] = hash_tree
end
end
@date = hash_tree
@date[month][day][hours][min][sec] = 1
With a custom class:
class HashTree < Hash
def initialize
super do |hash, key|
hash[key] = HashTree.new
end
end
end
@date = HashTree.new
@date[month][day][hours][min][sec] = 1
Compared to the lambda expression given above, this is simpler and also in one line:
Hash.new {|h,k| h[k] = Hash.new(&h.default_proc) }
->f{f[f]}[->f{Hash.new{|h,k|h[k]=f[f]}}]
Obviously.
Using symbols seemed to work:
ree-1.8.7-2009.10 > @date = {:month =>{:day => {:hours => {:min => {:sec => 1 } } } } }
=> {:month=>{:day=>{:hours=>{:min=>{:sec=>1}}}}}
I can then retrieve the val like this:
ree-1.8.7-2009.10 > @date[:month][:day]
=> {:hours=>{:min=>{:sec=>1}}}
It doesn't look like Ruby can do autovivification from the start, but you can easily add in that functionality. A search for "ruby autovivification" on Google gives:
http://t-a-w.blogspot.com/2006/07/autovivification-in-ruby.html
Which contains a decent example of how to create a hash that will work the way you are looking for.
ruby hash autovivification (facets) might also be helpful.
You can use the Facets gem's Hash.autonew
to do the same thing as the recursive
function given in Molf's answer.
1) Refinements
You can use the Ruby Refinements instead of Monkey Patching.
It is way more secure than MonkeyPatching the entire Hash class and the usage part is still fancy
精彩评论