需要一种更简单的方法来初始化 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]
是否是哈希。
我是 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]
是否是哈希。