user3206440 user3206440 - 23 days ago 6
Ruby Question

Ruby hash lookup with multiple keys

I have two array of hashes -

car_model
&
car_cc
as below. For each hash in
car_model
I need to lookup for the
cc
key and add that to
car_model
.

car_model = [
{state: "MH", regno: 5555, model: "alto"},
{state: "MH", regno: 5566, model: "alto"},
{state: "DL", regno: 5555, model: "prius"},
{state: "DL", regno: 5567, model: "nano"}
]

car_cc = [
{state: "MH", regno: 5555, cc: 999},
{state: "MH", regno: 5588, cc: 1800},
{state: "DL", regno: 5555, cc: 1119},
{state: "DL", regno: 5567, cc: nil}
]


Right now I use a regular
.each
loop to lookup for the
cc
key in
car_cc
and add that to each item in
car_model
.

car_model.each do |cm|
car_cc.each do |cc|
if(cm["state"]==cc["state"] && cm["regno"]==cc["regno"])
cm["cc"] = cc["cc"]
break
end
end
end


expected output

puts car_model
{:state=>"MH", :regno=>5555, :model=>"alto", :cc=>999}
{:state=>"MH", :regno=>5566, :model=>"alto", :cc=>nil}
{:state=>"DL", :regno=>5555, :model=>"prius", :cc=>1119}
{:state=>"DL", :regno=>5567, :model=>"nano", :cc=>nil}
=> nil
irb(main):008:0>


Is there a more efficient way of doing this - faster and a more rubyistic way ?

Answer

One way would be to convert car_cc to a Hash with convenient keys:

cc = car_cc.each_with_object({}) { |car, h| h[car.values_at(:state, :regno)] = car[:cc] }

so that you can perform the join easier:

car_model.each { |h| h[:cc] = cc[h.values_at(:state, :regno)] }

This assumes that the :state/:regno pairs are unique in car_cc and that you want to modify car_model. If you don't want to modify car_model then you could say:

car_model_cc = car_model.map { |cm| cm.merge(cc: cc[cm.values_at(:state, :regno)]) }

to copy everything while adding the :ccs.


Of course with data sets this small any performance differences will be too small to worry about and if your data sets are much larger then you'd probably want to stuff it all in a database and let the database do the heavy lifting.