Jason Waldrip
Jason Waldrip

Reputation: 5148

Deep Hash Inspection

Okay so this is a bit complex but I need to loop through this hash to find if each element meets one of following conditions:

The Value is a String

or

The Value is a Hash, containing Strings, Not Hashes

or

The Value is a Hash, containing Strings and/or Hashes

or

The Value is an Array

UPDATE:

Something very strange... [EDIT: this is because #collect is returning the hash's key-value pairs, which are arrays]

irb> my_hash['gd:name'].collect(&:class)
=> [Array, Array]

Looking at the hash below should the class of that inner element be a Hash, not an Array?

Inspected it looks like:

irb> my_hash['gd:name'].collect(&:inspect)
=> ["[\"gd:givenName\", {\"$t\"=>\"Thomas\"}]", "[\"gd:familyName\", {\"$t\"=>\"Chapin\"}]"]

BUT IT BEHAVES LIKE A HASH:

irb> my_hash['gd:name']['gd:givenName']
=> {"$t"=>"Thomas"}
irb> my_hash['gd:name']['gd:givenName']['$t']
=> "Thomas"

{
  "gd:etag"=>"\"Rnk7fjVSLyt7I2A9WhVQEU4KRQI.\"",
  "id"=>{
    "$t"=>"da513d38e88d949"
   },
  "gd:name"=>{
    "gd:givenName"=>{"$t"=>"Thomas"}, "gd:familyName"=>{"$t"=>"Chapin"}
  },
  "gd:phoneNumber"=>[
    {
      "rel"=>"mobile",
       "$t"=>"(480) 703-4887"
    }
  ], 
  "gd:email"=>[
    {
      "rel"=>"home",
      "primary"=>"true",
       "address"=>"[email protected]"
    }, 
    {
      "rel"=>"work",
      "address"=>"[email protected]"}
  ]
}

Upvotes: 1

Views: 752

Answers (2)

joelparkerhenderson
joelparkerhenderson

Reputation: 35483

Examine Hash values.

Return true iff each:

  • Value is a string or an array   
  • Value is a hash that is non-empty and contains only strings and/or hashes

A solution with some printing to show what's happening:

puts hash.each_pair.all?{|k,v|
  puts "key #{k} = value #{v} class #{v.class}"
  case v
  when String, Array
    true
  when Hash
    return false if v.empty? 
    v.each_pair.all?{|k2,v2|
      puts "inner key #{k2} = value #{v2} class #{v2.class}"
      case v2
      when String, Hash
        true
      else
        false
      end
    }
  else
    false
  end
}

If you don't care to keep track of the keys, you can change #each_pair to #values

Upvotes: 1

Alex Rockwell
Alex Rockwell

Reputation: 1474

You can test the class of hash value.

hash.each do |key, value|
  if value.is_a? String 
    # do String work
  elsif value.is_a? Hash
    #do Hash work
  elsif value.is_a? Array
    # do Array work
  end
end

EDIT: updated with suggestion from Niklas B.

Upvotes: 1

Related Questions