ruby 在包含任意数量的嵌套散列和数组的散列深处查找键/值对
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8301566/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Find key/value pairs deep inside a hash containing an arbitrary number of nested hashes and arrays
提问by steven_noble
A web service is returning a hash that contains an unknown number of nested hashes, some of which contain an array, which in turn contains an unknown number of nested hashes.
Web 服务正在返回一个哈希,其中包含未知数量的嵌套哈希,其中一些包含一个数组,而数组又包含未知数量的嵌套哈希。
Some of the keys are not unique -- i.e. are present in more than one of the nested hashes.
一些键不是唯一的——即存在于多个嵌套散列中。
However, all the keys that I actually care about are all unique.
但是,我真正关心的所有键都是独一无二的。
Is there someway I can give a key to the top-level hash, and get back it's value even if the key-value pair is buried deep in this morass?
有什么办法可以给顶级散列一个键,即使键值对深埋在这个泥潭中,也可以取回它的值吗?
(The web service is Amazon Product Advertising API, which slightly varies the structure of the results that it gives depending on the number of results and the search types permitted in each product category.)
(该 Web 服务是 Amazon Product Advertising API,根据结果数量和每个产品类别中允许的搜索类型,它提供的结果结构略有不同。)
采纳答案by Phrogz
Here's a simple recursive solution:
这是一个简单的递归解决方案:
def nested_hash_value(obj,key)
if obj.respond_to?(:key?) && obj.key?(key)
obj[key]
elsif obj.respond_to?(:each)
r = nil
obj.find{ |*a| r=nested_hash_value(a.last,key) }
r
end
end
h = { foo:[1,2,[3,4],{a:{bar:42}}] }
p nested_hash_value(h,:bar)
#=> 42
回答by denis.peplin
No need for monkey patching, just use Hashie gem: https://github.com/intridea/hashie#deepfind
不需要猴子补丁,只需使用Hashie gem:https: //github.com/intridea/hashie#deepfind
user = {
name: { first: 'Bob', last: 'Boberts' },
groups: [
{ name: 'Rubyists' },
{ name: 'Open source enthusiasts' }
]
}
user.extend Hashie::Extensions::DeepFind
user.deep_find(:name) #=> { first: 'Bob', last: 'Boberts' }
For arbitrary Enumerable objects, there is another extension available, DeepLocate: https://github.com/intridea/hashie#deeplocate
对于任意 Enumerable 对象,还有另一个可用的扩展,DeepLocate:https: //github.com/intridea/hashie#deeplocate
回答by barelyknown
Combining a few of the answers and comments above:
结合上面的一些答案和评论:
class Hash
def deep_find(key, object=self, found=nil)
if object.respond_to?(:key?) && object.key?(key)
return object[key]
elsif object.is_a? Enumerable
object.find { |*a| found = deep_find(key, a.last) }
return found
end
end
end
回答by Andy Triggs
Despite this appearing to be a common problem, I've just spent a while trying to find/come up with exactly what I need, which I think is the same as your requirement. Neither of the links in the first response are spot-on.
尽管这似乎是一个常见问题,但我只是花了一段时间试图找到/想出我需要的东西,我认为这与您的要求相同。第一个响应中的两个链接都不是正确的。
class Hash
def deep_find(key)
key?(key) ? self[key] : self.values.inject(nil) {|memo, v| memo ||= v.deep_find(key) if v.respond_to?(:deep_find) }
end
end
So given:
所以给出:
hash = {:get_transaction_list_response => { :get_transaction_list_return => { :transaction => [ { ...
The following:
下列:
hash.deep_find(:transaction)
will find the array associated with the :transaction key.
将找到与 :transaction 键关联的数组。
This is not optimal as the inject will continue to iterate even if memois populated.
这不是最佳的,因为即使填充了备忘录,注入也会继续迭代。
回答by ReggieB
A variation of barelyknown's solution: This will find all the values for a key in a hash rather than the first match.
几乎不知道的解决方案的一种变体:这将在散列中找到一个键的所有值,而不是第一个匹配项。
class Hash
def deep_find(key, object=self, found=[])
if object.respond_to?(:key?) && object.key?(key)
found << object[key]
end
if object.is_a? Enumerable
found << object.collect { |*a| deep_find(key, a.last) }
end
found.flatten.compact
end
end
{a: [{b: 1}, {b: 2}]}.deep_find(:b)will return [1, 2]
{a: [{b: 1}, {b: 2}]}.deep_find(:b)将返回 [1, 2]
回答by Chris Edwards
回答by paulz
Because Rails 5 ActionController::Parameters no longer inherits from Hash, I've had to modify the method and make it specific to parameters.
由于 Rails 5 ActionController::Parameters 不再继承自 Hash,我不得不修改该方法并使其特定于参数。
module ActionController
class Parameters
def deep_find(key, object=self, found=nil)
if object.respond_to?(:key?) && object.key?(key)
return object[key]
elsif object.respond_to?(:each)
object = object.to_unsafe_h if object.is_a?(ActionController::Parameters)
object.find { |*a| found = deep_find(key, a.last) }
return found
end
end
end
end
If the key is found, it returns the value of that key, but it doesn't return an ActionController::Parameter object so Strong Parameters are not preserved.
如果找到该键,它会返回该键的值,但不会返回 ActionController::Parameter 对象,因此不会保留强参数。
回答by Kapil Aggarwal
I use the following code
我使用以下代码
def search_hash(hash, key)
return hash[key] if hash.assoc(key)
hash.delete_if{|key, value| value.class != Hash}
new_hash = Hash.new
hash.each_value {|values| new_hash.merge!(values)}
unless new_hash.empty?
search_hash(new_hash, key)
end
end
回答by DaniG2k
I ended up using this for a small trie search I wrote:
我最终使用它进行了我写的小型搜索:
def trie_search(str, obj=self)
if str.length <= 1
obj[str]
else
str_array = str.chars
next_trie = obj[str_array.shift]
next_trie ? trie_search(str_array.join, next_trie) : nil
end
end
Note: this is just for nested hashes at the moment. Currently no array support.
注意:目前这仅适用于嵌套哈希。目前不支持阵列。

