简体   繁体   English

如何查找arrays嵌套的hash和hash嵌套的hash并从父节点返回多个匹配对象?

[英]How to search nested hash of arrays and arrays of hash and return multiple matching objects from the parent node?

Say I have the below ruby hash nested假设我嵌套了以下 ruby hash

hash_or_array = [{
  "book1" => "buyer1",
  "book2" => {
    "book21" => "buyer21", "book22" => ["buyer23", "buyer24", true]
  },
  "book3" => {
    "0" => "buyer31", "1" => "buyer32", "2" => "buyer33", 
    "3" => [{
      "4" => "buyer34",
      "5" => [10, 11],
      "6" => [{
        "7" => "buyer35"
      }]
    }]
  },
  "book4" => ["buyer41", "buyer42", "buyer43", "buyer35"],
  "book5" => {
    "book5,1" => "buyer5"
  }
}]

And I want to look for the string buyer35 .我想查找字符串buyer35 Upon match, it should return the following匹配后,它应该返回以下内容

[
    {
        "book3" => {
            "0" => "buyer31", "1" => "buyer32", "2" => "buyer33", 
            "3" => [{
                "4" => "buyer34",
                "5" => [10, 11],
                "6" => [{
                    "7" => "buyer35"
                }]
            }]
        }
    },
    {
        "book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
    }
]

The solution below (from another SO question, link below), returns the first match, but I am trying to figure out how to return multiple matches下面的解决方案(来自另一个 SO 问题,下面的链接),返回第一个匹配项,但我试图弄清楚如何返回多个匹配项

def recurse(obj, target)
  case obj
  when Array
    obj.each do |e|
      case e
      when Array, Hash
        rv = recurse(e, target)
        return [rv] unless rv.nil?
      when target
        return e
      end
    end
  when Hash
    obj.each do |k,v|
      case v
      when Array, Hash
        rv = recurse(v, target)
        return {k=>rv} unless rv.nil?
      when target
        return {k=>v}
      end
    end
  end
  nil
end

This is the original question and answer: How to search nested hash of arrays and arrays of hash and only return matching object from the parent node?这是原问答: How to search nested hash of arrays and arrays of hash and only return matching object from the parent node?

UPDATE: The correct return format should be更新:正确的返回格式应该是

[
    {
        "book3" => {
            "3" => [{
                "6" => [{
                    "7" => "buyer35"
                }]
            }]
        }
    },
    {
        "book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
    }
]

Here is a function that recursively searches for the target value in any nested arrays or hashes.这是一个 function 递归搜索任何嵌套的 arrays 或哈希中的目标值。 The function is then used to select the entries in your top level hash_or_array for entries that contain the target and adds them to an array. function 然后用于 select 包含目标的条目的顶级 hash_or_array 中的条目并将它们添加到数组中。

require 'pp'

def find_value(obj, target, found = false)
  found = true if obj == target
  return found unless obj.is_a?(Array) || obj.is_a?(Hash)
  case obj
  when Hash 
    obj.each { |k, v| found = find_value(v, target, found) }
  when Array
    obj.each { |v| found = find_value(v, target, found) }
  end
  found
end

found_entries = hash_or_array.inject([]) {|entries, obj| entries << obj.select { |k, v| find_value({ k => v }, "buyer35") }}

pp found_entries

=> =>

[{"book3"=>
   {"0"=>"buyer31",
    "1"=>"buyer32",
    "2"=>"buyer33",
    "3"=>[{"4"=>"buyer34", "5"=>[10, 11], "6"=>[{"7"=>"buyer35"}]}]},
  "book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]

The code below appears to generate the desired output for this specific case:下面的代码似乎为这个特定案例生成了所需的 output:

hash_or_array.inject([]) do |result, x|
  x.keep_if { |k, v| v.to_s =~ /buyer35/ }
  result << x
end

Here is a recursive solution to your "real" question.这是您的“真实”问题的递归解决方案。 Since it mutates the original object, I used a "trick" to make a deep copy first.由于它变异了原来的 object,我用了一个“技巧”先做了一个深拷贝。 The keep_entries_with produces an object with the original shape as your input, but since your output shape is different the second step just transforms the filtered result into the shape of your desired output. keep_entries_with生成一个 object,原始形状作为您的输入,但由于您的 output 形状不同,第二步只是将过滤后的结果转换为您想要的 output 的形状。

deep_copy = Marshal.load(Marshal.dump(hash_or_array))

def keep_entries_with(target, obj)
  return unless obj.is_a?(Hash) || obj.is_a?(Array)
  case obj
  when Hash
    keep_entries_with(target, obj.values)
    obj.keep_if do |k, v|
      v == target ||
      v.is_a?(Hash) && v.values.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) } ||
      v.is_a?(Array) && v.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) }
    end
  when Array
    obj.each do |v|
      keep_entries_with(target, v)
    end
  end
end

filtered = keep_entries_with("buyer35", deep_copy)

final_result = filtered.first.map { |k, v| { k => v } }
pp final_result

which produces:产生:

[{"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}},
 {"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM