使用多个键进行Ruby哈希查找

时间:2016-11-17 02:13:29

标签: ruby hashtable

我有两个哈希数组 - car_model& car_cc如下。对于car_model中的每个哈希,我需要查找cc密钥并将其添加到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}
]

现在我使用常规的.each循环来查找cc中的car_cc密钥,并将其添加到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
预期产出
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>

有更有效的方法吗 - 更快,更红宝石的方式?

3 个答案:

答案 0 :(得分:4)

一种方法是使用方便的键将car_cc转换为哈希:

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

这样您就可以更轻松地执行连接:

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

这假设:state / :regno对在car_cc中是唯一的,并且您要修改car_model。如果您不想修改car_model,那么您可以说:

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

在添加:cc时复制所有内容。

当然,对于数据集这么小,任何性能差异都太小而不用担心,如果你的数据集要大得多,那么你可能想把它全部填入数据库中,让数据库完成繁重的任务。

答案 1 :(得分:2)

car_model.map do |cm|
  cm.merge(car_cc.detect do |e|
             e[:state] == cm[:state] && e[:regno] == cm[:regno]
           end || {cc: nil})
end

或,DRY:

VALS = %i|state regno|
car_model.map do |cm|
  cm.merge(car_cc.detect do |e|
             [e, cm].map { |e| e.values_at(*VALS) }.reduce(:==)
           end || {cc: nil})
end

如果要查找的项目数量足够大,我将从构建中间对象开始:

map = car_cc.group_by { |e| e.values_at(:state, :regno) }
            .map { |k, v| [k, v.first[:cc]] }.to_h
#⇒ {
#    [ "MH", 5555 ] => 999,
#    [ "MH", 5588 ] => 1800,
#    [ "DL", 5555 ] => 1119,
#    [ "DL", 5567 ] => nil
# }

现在一切顺利:

car_model.each do |cm|
  cm[:cc] = map[[cm[:state], cm[:regno]]]
end

答案 2 :(得分:1)

model = car_model.each_with_object({}) { |g,h|
  h[g.values_at(:state, :regno)] = g.merge(:cc => nil) }
  #=> {["MH", 5555]=>{:state=>"MH", :regno=>5555, :model=>"alto", :cc=>nil},
  #    ["MH", 5566]=>{:state=>"MH", :regno=>5566, :model=>"alto", :cc=>nil},
  #    ["DL", 5555]=>{:state=>"DL", :regno=>5555, :model=>"prius", :cc=>nil},
  #    ["DL", 5567]=>{:state=>"DL", :regno=>5567, :model=>"nano", :cc=>nil}} 

cc = car_cc.each_with_object({}) { |g,h| h[g.values_at(:state, :regno)] = g }.
  select { |k,_| model.key?(k) }
  #=> {["MH", 5555]=>{:state=>"MH", :regno=>5555, :cc=>999},
  #    ["DL", 5555]=>{:state=>"DL", :regno=>5555, :cc=>1119},
  #    ["DL", 5567]=>{:state=>"DL", :regno=>5567, :cc=>nil}} 

(model.merge(cc) { |_,o,n| o.merge(n) }).values 
  #=> [{: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}] 

备注

在计算cc时,我们首先计算以下哈希值,然后删除k,v不包含密钥model的键值对k

car_cc.each_with_object({}) { |g,h| h[g.values_at(:state, :regno)] = g }
  #=> {["MH", 5555]=>{:state=>"MH", :regno=>5555, :cc=>999},
  #    ["MH", 5588]=>{:state=>"MH", :regno=>5588, :cc=>1800},
  #    ["DL", 5555]=>{:state=>"DL", :regno=>5555, :cc=>1119},
  #    ["DL", 5567]=>{:state=>"DL", :regno=>5567, :cc=>nil}} 

在最后一行代码中,我们在提取其值之前计算以下哈希值。

model.merge(cc) { |_,o,n| o.merge(n) }
  #=> {["MH", 5555]=>{:state=>"MH", :regno=>5555, :model=>"alto", :cc=>999},
  #    ["MH", 5566]=>{:state=>"MH", :regno=>5566, :model=>"alto", :cc=>nil},
  #    ["DL", 5555]=>{:state=>"DL", :regno=>5555, :model=>"prius", :cc=>1119},
  #    ["DL", 5567]=>{:state=>"DL", :regno=>5567, :model=>"nano", :cc=>nil}}

最后一次计算使用Hash#merge的形式,它使用一个块来确定合并的两个哈希中存在的键的值。有关三个块变量的解释,请参阅doc(此处_on,第一个是下划线,仅表示该块变量未在块计算中使用)