我在尝试使用密钥更新嵌套哈希时遇到问题。
我拥有的嵌套哈希是这样的:
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")
如果我能像下面那样动态调用“发送”方法真的很棒(显然它不会起作用)。
main_hash.send(:try, 'send(:fetch, "Farming").send(:fetch, "children").send(:fetch, "Organic Gas").send(:fetch, "children")')
我希望它能说明我想要实现的目标。我已经完成了所有Ruby Hash的内置功能,但我无法获得适合我需要的功能。
非常感谢任何帮助。
干杯。
答案 0 :(得分:4)
我不确定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
而不是{{1如果您的树结构可以包含其他对象(如字符串),则返回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(...)
这样,您保留原始树,但返回一个新树,并添加了额外的子项。
答案 1 :(得分:2)
要回答原始问题,您可以使用XKeys gem遍历动态路径,如下所示:
require 'xkeys' # on rubygems.org
main_hash.extend XKeys::Hash
path = ['Farming', 'children', 'Organic Gas', 'children']
main_hash[*path].merge!(another_hash)
我还建议使用:孩子而不是'孩子',以避免有大量重复的字符串。