【问题标题】:How to merge two arrays of hashes by the same pair of key and value ruby如何通过同一对键和值 ruby​​ 合并两个哈希数组
【发布时间】:2016-10-26 07:07:02
【问题描述】:

我是红宝石新手。我有两个哈希:

f = { "server"=>[{ "hostname"=>"a1", "ip"=>"10" }, {"hostname"=>"b1", "ip"=>"10.1" }] }
g = { "admin" =>[{ "name"=>"adam", "mail"=>"any", "hostname"=>"a1" },
                 { "name"=>"mike", "mail"=>"id", "hostname"=>"b1"}]}

我想得到另一个像这样的哈希:

{ "data" => [{"hostname"=>"a1", "ip"=>"10", "name" =>"adam", "mail"=>"any"},
             {"hostname"=>"b1", "ip"=>"10.1", "name" =>"mike", "mail"=>"id"}]}

"hostname"=>"something" 对始终在两个数组的哈希中匹配。我尝试过这样的事情:

data = server.merge(admin)

但这并不容易,而且正如您所期望的那样,它不起作用。你能帮我合并这些哈希值并为将来解释你是如何做到的吗?

【问题讨论】:

  • 我希望你不介意我编辑了你的问题。其中一个输入散列有错误(= 应该是 =>),我已将变量(fg)分配给每个输入散列,以便读者可以在 cmets 和答案中引用这些变量无需定义它们。

标签: arrays ruby hash merge


【解决方案1】:

我现在能想到的快速方法如下:

servers = { "server" => [{"hostname"=>"a1", "ip"=>"10"}, {"hostname"=>"b1", "ip"=>"10.1"}]}
admins = { "data" => [{"hostname"=>"a1", "ip"=>"10", "name" =>"adam", "mail"=>"any"}, {"hostname"=>"b1", "ip"=>"10.1", "name" =>"mike", "mail"=>"id"}]}
# FYI: you can just use arrays for representing the above data, you don't necessarily need a hash.
list_of_entries = (servers.values + admins.values).flatten
grouped_by_hostname_entries = list_of_entries.group_by { |h| h['hostname'] }
grouped_by_hostname_entries.map { |_, values| values.inject({}, :merge) }
#=> [{"hostname"=>"a1", "ip"=>"10", "name"=>"adam", "mail"=>"any"}, {"hostname"=>"b1", "ip"=>"10.1", "name"=>"mike", "mail"=>"id"}]

【讨论】:

    【解决方案2】:

    作为另一种变体,你可以试试这个

    h1 = { "server" => [{"hostname"=>"a1", "ip"=>"10"}, {"hostname"=>"b1", "ip"=>"10.1"}]}
    h2 = { "admin"  => [{"name" =>"adam", "mail"=>"any", "hostname"=>"a1"}, {"name" =>"mike", "mail"=>"id", "hostname"=>"b1"}]}
    h1['server'].zip(h2['admin']).map { |ar| ar.first.merge(ar.last) }
    
    #=> [{"hostname"=>"a1", "ip"=>"10", "name"=>"adam", "mail"=>"any"}, {"hostname"=>"b1", "ip"=>"10.1", "name"=>"mike", "mail"=>"id"}]
    

    zip 让我们同时遍历两个或多个数组。
    我们使用map 来返回结果。

    map 块中ar 将是相等的

    • [{"hostname"=>"a1", "ip"=>"10"}, {"name"=>"adam", "mail"=>"any", "hostname"=>"a1"}]
    • [{"hostname"=>"b1", "ip"=>"10.1"}, {"name"=>"mike", "mail"=>"id", "hostname"=>"b1"}]

    所以ar.first 将是{"hostname"=>"a1", "ip"=>"10"}ar.last 将是{"name"=>"adam", "mail"=>"any", "hostname"=>"a1"}

    最后我们使用merge 来组合两个哈希。
    希望这会有所帮助。

    【讨论】:

    • 假设h2["admin"].reverse!; h2 #=> [{"name"=>"mike", "mail"=>"id", "hostname"=>"b1"}, {"name"=>"adam", "mail"=>"any", "hostname"=>"a1"}]。然后h1['server'].zip(h2['admin']).map { |ar| ar.first.merge(ar.last) } #=> [{"hostname"=>"b1", "ip"=>"10", "name"=>"mike", "mail"=>"id"}, {"hostname"=>"a1", "ip"=>"10.1", "name"=>"adam", "mail"=>"any"}],这当然是不正确的。然而,一切都不会丢失,因为您只需要对一个或两个数组进行预排序,以便它们在 "hostname" 的值上排序相同。
    • 感谢您的通知。我没想到。
    【解决方案3】:

    代码和示例

    ff = f["server"].each_with_object({}) { |g,h| h[g["hostname"]] = g }
      #=> {"a1"=>{"hostname"=>"a1", "ip"=>"10"}, "b1"=>{"hostname"=>"b1", "ip"=>"10.1"}}
    
    { "data"=>g["admin"].map { |h| h.merge(ff[h["hostname"]]) } } 
      #=> {"data"=>[{"name"=>"adam", "mail"=>"any", "hostname"=>"a1", "ip"=>"10"},
      #             {"name"=>"mike", "mail"=>"id", "hostname"=>"b1", "ip"=>"10.1"}]} 
    

    说明

    我们想产生一个哈希

    { "data"=>arr }
    

    在哪里

    arr #=> [{ "name"=>"adam", "mail"=>"any", "hostname"=>"a1", "ip"=>"10" },
        #    { "name"=>"mike", "mail"=>"id", "hostname"=>"b1", "ip"=>"10.1" }]
    

    所以我们只需要计算arr

    首先,我们创建哈希

    ff = f["server"].each_with_object({}) { |g,h| h[g["hostname"]] = g }
      #=> {"a1"=>{"hostname"=>"a1", "ip"=>"10"}, "b1"=>{"hostname"=>"b1", "ip"=>"10.1"}}
    

    我们有

    enum = f["server"].each_with_object({})
      #=> #<Enumerator: [{"hostname"=>"a1", "ip"=>"10"},
      #                  {"hostname"=>"b1", "ip"=>"10.1"}]:each_with_object({})>
    

    我们可以通过将其转换为数组来查看将由该枚举器生成(并传递给其块)的元素:

    enum.to_a
      #=> [[{"hostname"=>"a1", "ip"=>"10"}, {}],
      #    [{"hostname"=>"b1", "ip"=>"10.1"}, {}]] 
    

    注意

    enum.each { |g,h| h[g["hostname"]] = g }
      #=> {"a1"=>{"hostname"=>"a1", "ip"=>"10"},
      #    "b1"=>{"hostname"=>"b1", "ip"=>"10.1"}} 
    

    each 传递enum 的第一个元素并使用并行赋值(也称为多重赋值)分配块变量:

    g,h = enum.next
      #=> [{"hostname"=>"a1", "ip"=>"10"}, {}] 
    g #=> {"hostname"=>"a1", "ip"=>"10"} 
    h #=> {} 
    

    我们现在可以执行块计算:

    h[g["hostname"]] = g
      #=> h["a1"] = {"hostname"=>"a1", "ip"=>"10"} 
      #=> {"hostname"=>"a1", "ip"=>"10"}
    

    返回值是块变量h的新值。然后将enum的第二个元素传递给block,进行block计算:

    g,h = enum.next
      #=> [{"hostname"=>"b1", "ip"=>"10.1"}, {"a1"=>{"hostname"=>"a1", "ip"=>"10"}}] 
    g #=> {"hostname"=>"b1", "ip"=>"10.1"} 
    h #=> {"a1"=>{"hostname"=>"a1", "ip"=>"10"}} 
    

    请注意,哈希 h 已更新。

    h[g["hostname"]] = g
      #=> {"hostname"=>"b1", "ip"=>"10.1"} 
    

    那么现在

    h #=> {"a1"=>{"hostname"=>"a1", "ip"=>"10"},
      #    "b1"=>{"hostname"=>"b1", "ip"=>"10.1"}} 
    

    ff #=> {"a1"=>{"hostname"=>"a1", "ip"=>"10"}, "b1"=>{"hostname"=>"b1", "ip"=>"10.1"}}
    

    现在我们可以计算arr

    g["admin"].map { |h| h.merge(ff[h["hostname"]]) }
    

    g["admin"]的第一个元素被传递给block并赋值给block变量:

    h = g["admin"][0]
      #=> {"name"=>"adam", "mail"=>"any", "hostname"=>"a1"}
    

    并进行块计算:

    h.merge(ff[h["hostname"]])
      #=> h.merge(ff["a1"])
      #=> h.merge({"hostname"=>"a1", "ip"=>"10"})
      #=> {"name"=>"adam", "mail"=>"any", "hostname"=>"a1", "ip"=>"10"} 
    

    然后

    h = g["admin"][1]
      #=> {"name"=>"mike", "mail"=>"id", "hostname"=>"b1"} 
    
    h.merge(ff[h["hostname"]])
      #=> h.merge(ff["b1"])
      #=> h.merge({"hostname"=>"a2", "ip"=>"10"})
      #=> {"name"=>"mike", "mail"=>"id", "hostname"=>"a2", "ip"=>"10"}
    

    因此,

    arr
      #=> [{"name"=>"adam", "mail"=>"any", "hostname"=>"a1", "ip"=>"10"},
      #=>  {"name"=>"mike", "mail"=>"id", "hostname"=>"b1", "ip"=>"10.1"}] 
    

    由块返回,我们完成了。

    【讨论】:

      【解决方案4】:
      f = { "server"=>[{ "hostname"=>"a1", "ip"=>"10" }, 
      {"hostname"=>"b1",   "ip"=>"10.1" }] }
      
      g = { "admin" =>[{ "name"=>"adam", "mail"=>"any", "hostname"=>"a1" },
      { "name"=>"mike", "mail"=>"id", "hostname"=>"b1"}]}
      
      # manual way
      host_admin_merge = []
      host_admin_merge << f["server"].first.merge(g["admin"].first)
      host_admin_merge << f["server"].last.merge(g["admin"].last)
      
      # a bit more automated, iterate, test key's value, append to new array
      host_admin_merge = []
      f["server"].each do |host|
        g["admin"].each do |admin|
          if admin[:hostname] == host[:hostname]
            host_admin_merge << host.merge(admin)
          end
        end
      end
      
      # assign the array to a hash with "data" as the key
      host_admin_hash = {}
      host_admin_hash["data"] = host_admin_merge
      p host_admin_hash
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-06-30
        • 1970-01-01
        • 2017-12-18
        • 2019-04-12
        • 2015-06-13
        • 2014-09-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多