How do I replace all the values in a hash with a new value?
Let's say I have an arbitrarily deep nested Hash h
:
h = {
:foo => { :bar => 1 },
:baz => 10,
:quux => { :swozz => {:muux => 1000}, :grimel => 200 }
# ...
}
And let's say I have a class C
defined as:
class C
attr_accessor :dict
end
How do I replace all nested values in h
so that they are now C
instances with the dict
attribute set to that value? For instance, in the above example, I'd expect to have something like:
h = {
:foo => <C @dict={:bar => 1}>,
:baz => 10,
:quux => <C @dict={:swozz => <C @dict={:muux => 1000}>, :grimel => 200}>
# ...
}
开发者_运维百科
where <C @dict = ...>
represents a C
instance with @dict = ...
. (Note that as soon as you reach a value which isn't nested, you stop wrapping it in C
instances.)
def convert_hash(h)
h.keys.each do |k|
if h[k].is_a? Hash
c = C.new
c.dict = convert_hash(h[k])
h[k] = c
end
end
h
end
If we override inspect
in C
to give a more friendly output like so:
def inspect
"<C @dict=#{dict.inspect}>"
end
and then run with your example h
this gives:
puts convert_hash(h).inspect
{:baz=>10, :quux=><C @dict={:grimel=>200,
:swozz=><C @dict={:muux=>1000}>}>, :foo=><C @dict={:bar=>1}>}
Also, if you add an initialize
method to C
for setting dict
:
def initialize(d=nil)
self.dict = d
end
then you can reduce the 3 lines in the middle of convert_hash
to just h[k] = C.new(convert_hash_h[k])
class C
attr_accessor :dict
def initialize(dict)
self.dict = dict
end
end
class Object
def convert_to_dict
C.new(self)
end
end
class Hash
def convert_to_dict
Hash[map {|k, v| [k, v.convert_to_dict] }]
end
end
p h.convert_to_dict
# => {
# => :foo => {
# => :bar => #<C:0x13adc18 @dict=1>
# => },
# => :baz => #<C:0x13adba0 @dict=10>,
# => :quux => {
# => :swozz => {
# => :muux => #<C:0x13adac8 @dict=1000>
# => },
# => :grimel => #<C:0x13ada50 @dict=200>
# => }
# => }
精彩评论