Home > Mobile >  How to search nested hash of arrays and arrays of hash and only return matching object from the pare
How to search nested hash of arrays and arrays of hash and only return matching object from the pare

Time:04-19

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"],
  "book5" => {
    "book5,1" => "buyer5"
  }
}]

And I want to search for a string that matches buyer35. On match, I want it to return the following result

"book3" => {
    "3" => [{
      "6" => [{
        "7" => "buyer35"
      }]
    }]
  }]

All, other non matching keys,values, arrays should be omitted. I have the following example, but it doesn't quite work

def search(hash)
    hash.each_with_object({}) do |(key, value), obj|
      if value.is_a?(Hash)
        returned_hash = search(value)
        obj[key] = returned_hash unless returned_hash.empty?
      elsif value.is_a?(Array)
        obj[key] = value if value.any? { |v| matches(v) }
      elsif matches(key) || matches(value)
        obj[key] = value
      end
    end
end


def matches(str)
    match_criteria = /#{Regexp.escape("buyer35")}/i
    (str.is_a?(String) || str == true || str == false) && str.to_s.match?(match_criteria)
end



....
=> search(hash_or_array)

Any help is appreciated. I realize, I need to use recursion, but can't quite figure how to build/keep track of the matched node from the parent node.

CodePudding user response:

You can use the following recursive method.

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
recurse(hash_or_array, "buyer35")
  #=> [{"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}}]
recurse(hash_or_array, "buyer24")
  #=>[{"book2"=>{"book22"=>"buyer24"}}]
recurse(hash_or_array, "buyer33")
  #=> [{"book3"=>{"2"=>"buyer33"}}]
recurse(hash_or_array, 11)
  #=> [{"book3"=>{"3"=>[{"5"=>11}]}}]
recurse(hash_or_array, "buyer5")
  #=>[{"book5"=>{"book5,1"=>"buyer5"}}]

If desired, one may write, for example,

recurse(hash_or_array, "buyer35").first
  #=> {"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}}
  • Related