需要一种更简单的方法来初始化 ruby 中的嵌套哈希,而不是在每个更深层次上进行赋值

need an easier way to initialize nested hashes in ruby rather than assignment at each deeper level

我是 ruby 领域的新手,发现使用哈希有很多困难。在 ruby 中似乎有很多方法可以做同样的事情,但我遇到的问题是在现有哈希中的子级别分配哈希。

例如,在我的 class 中,我有以下代码:

@query_hash = {"term" => {"field" => "value"}
@main_hash["query"]["filtered"]["query"]["bool"] = @query_hash

问题是 @main_hash 中有时会丢失其中一个子散列,因此上述路径会缺少 "filtered""query" 等键在 @main_hash 中,我会得到一个与 nil 相关的异常。所以我必须做的是首先检查每个嵌套级别,然后在我深入哈希时引用下一个嵌套级别。如果该级别不存在,我必须以如下丑陋的方式初始化该子哈希:@main_hash["query"] = {"filtered" => {"query" => "bool"}}

有没有更干净、更简单的方法来处理这种散列修改?

merge方法就是为这种东西而建的!

@main_hash = { "not_query" => "something else" }

@main_hash.merge!(
  "query" => {
    "filtered" => {
      "query" => {
        "bool" => {
          "term" => { "field" => "value" }
        }
      }
    }
  }
)

此调用后的 @main_hash 现在是这样的:

{"not_query"=>"something else", "query"=>{"filtered"=>{"query"=>{"bool"=>{"term"=>{"field"=>"value"}}}}}}

我只想使用默认过程创建哈希,将缺失值初始化为具有相同默认过程的子哈希:

h = Hash.new { |h,k| h[k] = Hash.new(&h.default_proc) } #=> {}
h['a']['b']['c'] = 'd'
h #=> {"a"=>{"b"=>{"c"=>"d"}}}

这是它的工作原理。

当您使用 Hash.new(而不是仅使用 {})创建新哈希时,您可以传递默认值或默认过程。

如果你传递一个值,那么任何时候你向哈希请求一个不存在的键,你都会得到那个值:

irb(main):001:0> counts = Hash.new(0)  #=> {}
irb(main):002:0> counts[:somekey] #=> 0

请注意,这实际上并没有将密钥插入到哈希中:

irb(main):003:0> counts.keys #=> []

这意味着任何类型的非基于赋值的修改都不会像您预期的那样工作:

lists = Hash.new([]);
%w<a b c b>.each do |k| lists[k].push k end
lists  #=> {}

每次您尝试访问 lists[k] 时,它 return 都是空列表,并附加到该列表,但该列表不会分配回 lists[k],因此哈希留空。

这是默认的 proc 派上用场的地方。您不是传递值,而是传递一段代码,每次访问不存在的键时,该代码块都是 运行。块的 return 值就像默认值一样被 returned,但代码可能有副作用,包括修改散列(作为第一个参数传递给 proc)。

lists = Hash.new { |hash,key| hash[key] = [] }
%w<a b c b>.each do |k| lists[k].push k end
lists # => {"a"=>["a"], "b"=>["b", "b"], "c"=>["c"]}

这就是我解决问题的方法:我分配了一个默认过程来存储和 returns 一个新的散列。但不仅仅是任何新哈希 - 它是一个新哈希,具有与包含哈希相同的默认过程,因此您可以使用单个表达式尽可能深入,并且它做正确的事情。

这只是@Ryan Bigg 回答的概括。

假设:

main_hash   = { "query1"=>
                 { "filtered"=>
                   { "query2"=>"value" }
                 }
              }
nested_keys = ["query1", "filtered", "cat", "dog"]
query_hash  = { "term"=>
                { "field"=>"value" }
              }

然后:

nested_keys.reduce(main_hash) { |h,k|
  h[k].is_a?(Hash) ? h[k] : h[k] = {} }.update(query_hash) 
main_hash
  #=> { "query1"=>
  #     { "filtered"=>
  #       { "query2"=> "value",
  #         "cat"=>
  #           { "dog"=>
  #             {"term"=>
  #               {"field"=>"value"}
  #             }
  #           }
  #       }
  #     }
  #   }

请注意,h[k].is_a?(Hash) 具有双重职责:它既检查 h 是否有键 k,如果有,则 h[k] 是否是哈希。