首页 文章

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

提问于
浏览
33

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

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

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

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

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

9 回答

  • 5

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

    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
    
  • 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
    
  • 0

    不需要猴子修补,只需使用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' }
    

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

  • 19

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

    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
    

    所以给出:

    hash = {:get_transaction_list_response => { :get_transaction_list_return => { :transaction => [ { ...
    

    下列:

    hash.deep_find(:transaction)
    

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

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

  • 0

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

    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) 将返回 [1, 2]

  • 9

    我使用以下代码

    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
    
  • 29

    我最后用这个用于我写的一个小小的搜索:

    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
    

    注意:这仅适用于嵌套哈希 . 目前没有阵列支持 .

  • 0

    因为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
    

    如果找到该键,则返回该键的值,但它不返回ActionController :: Parameter对象,因此不保留Strong Parameters .

  • 0

    Ruby 2.3引入了Hash#dig,它允许你这样做:

    h = { foo: {bar: {baz: 1}}}
    
    h.dig(:foo, :bar, :baz)           #=> 1
    h.dig(:foo, :zot)                 #=> nil
    

相关问题