在包含任意数量的嵌套哈希和数组的哈希深处查找键/值对

ste*_*ble 35 ruby arrays hash amazon nested

Web服务返回一个包含未知数量的嵌套哈希的哈希,其中一些包含一个数组,该数组又包含未知数量的嵌套哈希值.

一些键不是唯一的 - 即存在于多个嵌套的哈希中.

但是,我真正关心的所有键都是独一无二的.

有没有什么事情我可以给顶级哈希一个关键,并且即使键值对深埋在这个泥潭里,也要回到它的价值?

(该网络服务是亚马逊产品广告API,它根据每个产品类别中允许的结果数量和搜索类型略微改变其提供的结果结构.)

Phr*_*ogz 32

这是一个简单的递归解决方案:

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
Run Code Online (Sandbox Code Playgroud)

  • 这段代码导致我堆栈溢出.我想这是由于字符串和/或其他会响应`each`方法的东西.我将`elsif obj.respond_to?(:each)`改为`elsif obj.is_a?(Hash)或obj.is_a?(Array)`.现在它工作正常.谢谢你的解决方案. (7认同)
  • 如果这个东西打印出它的路径(面包屑?),它会很好... (2认同)

den*_*lin 25

不需要猴子修补,只需使用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' }
Run Code Online (Sandbox Code Playgroud)

对于任意Enumerable对象,还有另一个可用的扩展,DeepLo​​cate:https://github.com/intridea/hashie#deeplocate

  • 我发现Hashi :: Extensions :: DeepFind是一个很好的方法。而且,如果您要查找重复的键,那么deep_find_all()方法非常棒。强烈推荐。 (2认同)

bar*_*own 24

结合上面的一些答案和评论:

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
Run Code Online (Sandbox Code Playgroud)


And*_*ggs 9

尽管这似乎是一个常见的问题,但我花了一些时间试图找到/提出我需要的东西,我认为这与你的要求相同.第一个响应中的任何一个链接都没有点击.

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
Run Code Online (Sandbox Code Playgroud)

所以给出:

hash = {:get_transaction_list_response => { :get_transaction_list_return => { :transaction => [ { ... 
Run Code Online (Sandbox Code Playgroud)

下列:

hash.deep_find(:transaction)
Run Code Online (Sandbox Code Playgroud)

将找到与:transaction键关联的数组.

这不是最佳的,因为即使填充了备忘录,注入也将继续迭代.


Reg*_*ieB 8

勉强知道解决方案的变体:这将在哈希中找到密钥的所有值,而不是第一个匹配.

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
Run Code Online (Sandbox Code Playgroud)

{a: [{b: 1}, {b: 2}]}.deep_find(:b) 将返回 [1, 2]


Chr*_*rds 5

Ruby 2.3引入了Hash#dig,它允许您执行以下操作:

h = { foo: {bar: {baz: 1}}}

h.dig(:foo, :bar, :baz)           #=> 1
h.dig(:foo, :zot)                 #=> nil
Run Code Online (Sandbox Code Playgroud)