Yannick Schall
Yannick Schall

Reputation: 34869

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 myclass.price

Upvotes: 2

Views: 311

Answers (1)

mliebelt
mliebelt

Reputation: 15525

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">

Upvotes: 3

Related Questions