Home > Mobile >  How to search nested hash of arrays and arrays of hash and return multiple matching objects from the
How to search nested hash of arrays and arrays of hash and return multiple matching objects from the

Time:04-20

Say I have the below ruby hash nested

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. 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

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?

CodePudding user response:

The code below appears to generate the desired output for this specific case:

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

CodePudding user response:

Here is a function that recursively searches for the target value in any nested arrays or hashes. 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.

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"]}]
  • Related