recursion through hashes in ruby
I'm doing a bit of recursion through hashes to build attr_accessor and class instances. I get all of the value from a main hash. I use it to describe an event (dance class and club) and I'd like to be able to store the info like this:
data = {:datetime => '2011-11-23', :duration => '90', :lesson => {:price => '£7', :level => 'all'}, :club => {:price => "4"}
so that I can easily retrieve lesson[:price]
and club[:price]
.
With the recursion that I have in place, I check every item of the main hash to see if the value is a hash. If it is I restart the recursion and populate all of the values.
The problem is that I can't have 2 variables of the same name as lesson[:price]
collides with club[:price]
.
This is the recursion:
class Event
#attr_reader :datetime, :duration, :class, :price, :level
def init(data, recursion)
data.each do |name, value|
if value.is_a? Hash
init(value, recursion+1)
else
instance_variable_set("@#{name}", value)
self.class.send(:attr_accessor, name)
end
end
end
It skip the lesson and club level and add all of their inner values to the instance list.
Is it possible to actually append the name of skipped level so that I can access it through my_class.lesson.price
, myclass.club.price
instead of my开发者_运维技巧class.price
You will have to change the API you use currently. Here is the corrected code:
class Event
#attr_reader :datetime, :duration, :class, :price, :level
def init(data, stack = [])
data.each do |name, value|
if value.is_a? Hash
init(value, stack << name.to_s)
stack.pop
else
new_name = stack.empty? ? name : stack.join("_") + "_" + name.to_s
instance_variable_set("@#{new_name}", value)
self.class.send(:attr_accessor, new_name)
end
end
end
end
It is the following idea:
- Replace
recursion
it is not used anyway with a stack for the keys used. - Every time, you go into the recursion, the stack is appended the new key.
- Every time, the recursion is left, the stack is reduced (by using
pop
).
The code for appending the things together is ugly, but it works. The output after using your example data:
irb(main):042:0> e.init(data)
=> {:datetime=>"2011-11-23", :duration=>"90", :lesson=>{:price=>"7", :level=>"all"}, :club=>{:price=>"4"}}
irb(main):043:0> e
=> #<Event:0x2628360 @datetime="2011-11-23", @duration="90", @lesson_price="7", @lesson_level="all", @club_price="4">
精彩评论