Ruby 嵌套哈希动态获取

标签 ruby dynamic hash nested fetch

我在尝试使用 key 更新嵌套哈希时遇到问题。

我的嵌套哈希是这样的:

main_hash = {   
    "Energy"=>
      {"name"=>"Energy", "uri"=>"energy", 
      "children"=>
        {"Renewable Energy"=>{"name"=>"Renewable Energy", "uri"=>"energy/renewable_energy"}}}
    , 
    "Farming"=>
      {"name"=>"Farming", "uri"=>"farming", 
        "children"=>
        {"Organic Gas"=>
            {"name"=>"Organic Gas", "uri"=>"farming/organic_gas"
              "children" =>
                {"Gas Oil"=>{"name"=>"Gas Oil", "uri"=>"farming/organic_gas/gas_oil"}}
              }}}}

我想做的是从散列更新一个项目(例如,我想向“有机气体”添加另一个子项)。我知道我可以做到这一点:

  main_hash["Farming"]["children"]["Organic Gas"]["children"].merge!(another_hash)

问题是我需要动态获取它,因为它可以嵌套得很深。

因此,为了达到所需的水平,我会这样做(确实如上所示)。

main_hash.send(:fetch, "Farming").send(:fetch, "children").send(:fetch, "Organic Gas").send(:fetch, "children")

如果我能像下面这样动态地调用“send”方法就太好了(显然它不会起作用)。

main_hash.send(:try, 'send(:fetch, "Farming").send(:fetch, "children").send(:fetch, "Organic Gas").send(:fetch, "children")')

我希望它能清楚地说明我想要实现的目标。我已经遍历了 Ruby Hash 的所有内置函数,但找不到适合我需要的函数。

如有任何帮助,我们将不胜感激。

干杯。

最佳答案

我不确定哈希真的是这里最好的数据结构。你试图用它来表示一棵树,这很好,但如果你只是明确地把它变成一棵树,它可能会更清楚一点:

class Tree
  attr_reader :name, :uri, :children, :parent

  def initialize(name, uri, *children)
    @children = children
    @name, @uri = name, uri
  end

  def <<(child)
    @children << child
  end

  def find(name)
    each_branch.detect {|branch| branch.name == name }
  end

  def leaf?
    @children.empty?
  end

  # The parameter `i` just decides whether or not to include self.
  def each_branch( i=true, &blk )
    enum = Enumerator.new do |y|
      y.yield self if i
      @children.each do |c|
        next unless c.is_a? Tree
        y.yield c
        c.each_branch( false ).each {|b| y.yield b }
      end
    end
    block_given? ? enum.each( &blk ) : enum
  end

  # This yields each leaf and its parent.
  def each_leaf( parent=self, &blk )
    enum = Enumerator.new do |y|
      @children.each do |c|
        if !c.leaf?
          c.each_leaf( c ).each do |l,p|
            y.yield l, p
          end
        else y.yield c, parent
        end
      end
    end
    block_given? ? enum.each( &blk ) : enum
  end

end

(我只是从我之前制作的树结构中借用了那些枚举器 - each_leaf 方法可能也有帮助,您可以检查类是否不是 Tree 而不是 leaf? 返回 true 如果你有一个可以包含其他对象的树结构,比如字符串)。

然后你可以这样做:

root_tree = Tree.new "Farming", "farming"
root_tree << Tree.new( "Organic Gas", "organic_gas" )

gas = root_tree.find "Organic gas"
gas << Tree.new(...)

我认为这只是为工作找到正确数据结构的一个例子。即使 tree 方法的效率稍低,它也更清楚发生了什么,并且可能会减少动态代码中的错误。

如果问题是你不想修改原来的树,只想复制,那么只需要重新定义:

class Tree
  attr_accessor :children

  def <<(child)
    new_tree = self.dup
    new_tree.children = @children + [child]
    new_tree
  end
end

这样,您保留了原始树,但返回了一棵添加了额外 child 的新树。

关于Ruby 嵌套哈希动态获取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7667745/

相关文章:

ruby-on-rails - 站点中的层次结构

ruby-on-rails - Rspec 3 如何测试 flash 消息

javascript - React Router 将 props 传递给 <Link/> 渲染的组件?

c - 有没有办法在 c 中反转 crypt()?

python - 生成随机函数(与随机数相反)

ruby - gem install pg error : couldn't understand kern. osversion `14.0.0' on Yosemite w/Ruby 2.1.5

ruby-on-rails - Authlogic:如何阻止某些 IP 地址注册?

python - 更改 python matplotlib 颜色图动态范围为特定范围

javascript - 从 Soundcloud 嵌入的动态音频 &lt;iframe&gt;

hash - 对 Web 服务的密码加盐的原因