Donato Donato - 4 months ago 14
Ruby Question

How to use ruby's value_at to get subhashes in a hash

I have a simple collection:

{
:metadata=>{:latitude=>25.86222, :longitude=>-80.27901},
:components=>{:primary_number=>"4390", :street_predirection=>"E"},
:analysis=>{:footnotes=>"N#", :dpv_footnotes=>"AABB"}
}


What I want is this:

{:latitude=>25.86222, :longitude=>-80.27901, :primary_number=>"4390", :street_predirection=>"E",:footnotes=>"N#", :dpv_footnotes=>"AABB" }


In other words, I don't care about the first-level keys. I just care about everything else and I want to iterate through everything else as such:

my_hash.each {|k,v| ... }


I looked through the Hash class and felt that
values_at
will help simplify this task. So I try it:

Record.first.mail_validation
# => {:metadata=>{:county_name=>"Miami-Dade", :latitude=>25.86222, :longitude=>-80.27901, :time_zone=>"Eastern", :rdi=>"Residential"}, :components=>{:primary_number=>"4390", :street_predirection=>"E", :street_name=>"2nd", :street_suffix=>"Ave", :secondary_number=>nil, :secondary_designator=>nil, :city_name=>"Hialeah", :state_abbreviation=>"FL", :zipcode=>"33013", :plus4_code=>"2249"}, :analysis=>{:footnotes=>"N#", :dpv_footnotes=>"AABB", :dpv_match_code=>"Y"}}
Record.first.mail_validation.keys
# => [:metadata, :components, :analysis]


This returns no results, since the argument to values_at is a string and I pass an array:

Record.first.mail_validation.values_at(Record.first.mail_validation.keys)
# => [nil]


This doesn't work, because although I pass string as arguments to values_at, the keys in the hash are symbols, not keys:

Record.first.mail_validation.values_at(Record.first.mail_validation.keys.join(", "))
# => [nil]


Any suggestions on this?

Answer

Say you have a hash like hash = { a: 1, b: 2 }.

Then as you know you can use hash.keys to get [:a, :b].

How to pass these keys into values_at, which uses sequential parameters and not an array?

Use the splat operator, which will take the array's elemenets and use them for the sequential parameters. See here for more info.

For example:

hash.values_at(*hash.keys)

which returns [1, 2].

So in your case it would be almost the exact same as your original code

Instead of

Record.first.mail_validation.values_at(Record.first.mail_validation.keys)

you'd write

Record.first.mail_validation.values_at(*Record.first.mail_validation.keys)
Comments