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

6yt4nkrj  于 2023-02-18  发布在  Ruby
关注(0)|答案(9)|浏览(118)

Web服务返回的哈希包含未知数量的嵌套哈希,其中一些哈希包含数组,而数组又包含未知数量的嵌套哈希。
一些键不是唯一的--即存在于多个嵌套的散列中。
但是,所有我真正关心的键都是唯一的。
有没有什么方法可以给予顶级散列一个键,即使键-值对被深埋在这个沼泽中,也能取回它的值?
(The Web服务是Amazon Product Advertising API,它会根据每个产品类别中允许的结果数量和搜索类型,使其提供的结果结构略有不同。)

6tr1vspr

6tr1vspr1#

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

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
3qpi33ja

3qpi33ja2#

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

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

h22fl7wq

h22fl7wq3#

结合以上几个答案和评论:

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
fhg3lkii

fhg3lkii4#

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

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

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

2lpgd9685#

一个几乎不为人知的解的变体:这将在散列中查找键的所有值,而不是第一个匹配项。

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)将返回

uqjltbpv

uqjltbpv6#

尽管这似乎是一个常见的问题,我只是花了一段时间试图找到/拿出我到底需要什么,我认为这是相同的要求。没有一个链接在第一次回应是正确的。

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键关联的数组。
这不是最佳的,因为即使填充了 memo,注入也会继续迭代。

yrefmtwq

yrefmtwq7#

我使用以下代码

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
gblwokeq

gblwokeq8#

最后我用它做了一个小的trie搜索,我写道:

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

注意:这只是针对嵌套的散列,目前不支持数组。

72qzrwbm

72qzrwbm9#

因为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对象,因此不保留强参数。

相关问题