【问题标题】:Ruby hash lookup with multiple keys具有多个键的 Ruby 哈希查找
【发布时间】:2017-03-31 10:44:20
【问题描述】:

我有两个哈希数组 - car_modelcar_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 循环来查找car_cc 中的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>

有没有更有效的方法——更快、更红宝石?

【问题讨论】:

    标签: ruby hashtable


    【解决方案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 的形式,它使用一个块来确定在被合并的两个散列中存在的键的值。有关三个块变量的说明,请参阅文档(此处为 _on,第一个下划线仅表示该变量未用于块计算)。

    【讨论】:

    • 查看其他答案后,我得出的结论是,我的答案不如@muistooshort 给出的答案...
    • ...@mudasobwa。如果我没有将cc 合并到model 中,而是对model 的每个键使用find/detect 来查找cc 的键值对以合并到@987654338,那么我可以跳过select 操作@,就像 Mudsie 所做的那样,或者只是从 cc 中为键 :cc 添加适用的键键值对,就像 ? 所做的那样。
    【解决方案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
    

    或者,干:

    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
    

    【讨论】:

    • 使用detect的好主意。
    【解决方案3】:

    一种方法是将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)]) }
    

    复制所有内容,同时添加:ccs。


    当然,对于这么小的数据集,任何性能差异都会太小而无需担心,如果您的数据集大得多,那么您可能希望将它们全部填充到数据库中,让数据库完成繁重的工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-04
      • 2012-01-02
      • 2010-12-06
      • 2016-06-11
      相关资源
      最近更新 更多