通过红宝石中的哈希递归

时间:2011-09-23 12:55:30

标签: ruby recursion

我正在通过哈希进行一些递归来构建attr_accessor和类实例。 我从主哈希中获取所有值。 我用它来描述一个事件(舞蹈课和俱乐部),我希望能够存储这样的信息:

data = {:datetime => '2011-11-23', :duration => '90', :lesson => {:price => '£7', :level => 'all'}, :club => {:price => "4"}

这样我就可以轻松检索lesson[:price]club[:price]

使用我所具有的递归,我检查主散列的每个项目以查看该值是否为散列。如果是,我重新启动递归并填充所有值。 问题是我不能让2个与lesson[:price]同名的变量与club[:price]发生碰撞。

这是递归:

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

它跳过课程和俱乐部级别,并将所有内部值添加到实例列表中。

是否可以实际附加跳过级别的名称,以便我可以通过my_class.lesson.pricemyclass.club.price代替myclass.price

来访问它

1 个答案:

答案 0 :(得分:3)

您必须更改当前使用的API。这是更正后的代码:

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

这是以下想法:

  • 替换recursion无论如何都不会使用堆栈来使用密钥。
  • 每次进入递归时,堆栈都会附加新密钥。
  • 每次递归时,堆栈都会减少(使用pop)。

将所有内容附加在一起的代码很难看,但它确实有用。使用示例数据后的输出:

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