【问题标题】:Ruby looping over 2 hash arrays and comparing key valuesRuby 循环遍历 2 个哈希数组并比较键值
【发布时间】:2021-06-04 08:58:08
【问题描述】:

我在尝试遍历两个哈希数组时遇到问题。 我的哈希看起来像:

h1= [
  {"name"=>"postgresql", "version"=>29},
  {"name"=>"java", "version"=>8},
  {"name"=>"python", "version"=>3}
]

h2= [
  {"name"=>"postgresql", "release"=>"postgresql-release", "url"=>"https://github.com/repo/postgresql-release"},
  {"name"=>"java", "release"=>"java-release", "url"=>"https://github.com/repo/java-release"}
]

combined = []

在检查 h1 和 h2 中“name”字段的哈希值后,我需要将这两个哈希组合到一个名为 combined 的数组中,这样:

  • if h1["name"] == h2["name"] ---> 保存名称、版本、版本和网址。
  • 如果h1["name"] != h2["name"](例如:在h1中找到python但在h2中找不到)--->保存名称,版本,发布= nil,url= nil

combined 数组应如下所示:

combined = [
  {"name"=>"postgresql", "version"=>29, "release"=>"postgresql-release", "url"=>"https://github.com/repo/postgresql-release"},
  {"name"=>"java", "version"=>8, "release"=>"java-release", "url"=>"https://github.com/repo/java-release"},
  {"name"=> "python", "version"=>3, "release"=> nil, "url"=> nil}
]

我已经尝试了很多,并且总是有更多的迭代和/或在名称不同的情况下无法打印任何东西。

我的代码如下所示:

h1= [{"name"=>"postgresql", "version"=>29}, {"name"=>"java", "version"=>8}, {"name"=>"python", "version"=>3}]

h2= [{"name"=>"postgresql", "release"=>"postgresql-release", "url"=>"https://github.com/repo/postgresql-release"}, {"name"=>"java", "release"=>"java-release", "url"=>"https://github.com/repo/java-release"}]

combined = []

h1.each do |h1Key, h1Value|
  h2.each do |h2Key, h2Value|
    if h1["name"] == h2["name"]
      combined << {"name"=> h1["name"], "version"=> h1["version"], "release"=> h2["release"], "url"=> h2["url"]} 
    else
      combined << {"name"=> h1["name"], "version"=> h1["version"], "release"=> nil, "url"=> nil}
    end
  end
end
puts combined.to_s

我也无法正确比较 h1 和 h2 的值。

非常感谢您的帮助,因为我对 Ruby 还是很陌生,不幸的是我的逻辑并没有帮助我。

提前致谢! ^^

【问题讨论】:

    标签: ruby


    【解决方案1】:

    首先,h1 和 h2 是哈希数组,而不是哈希本身。如果你遍历它们,你会得到数组中的每个散列,而不是某个散列的键和值。

    为清楚起见,我们将它们重命名为 a1a2

    a1= [{"name"=>"postgresql", "version"=>29}, {"name"=>"java", "version"=>8}, {"name"=>"python", "version"=>3}]
    a2= [{"name"=>"postgresql", "release"=>"postgresql-release", "url"=>"https://github.com/repo/postgresql-release"}, {"name"=>"java", "release"=>"java-release", "url"=>"https://github.com/repo/java-release"}]
    
    combined = []
    

    现在,当我们遍历它们时,我们会依次得到每个哈希。

    a1.each do |h1|
    

    对于第一个数组中的每个散列,我们想在第二个数组中找到对应的散列。如果我们在第一个数组中查看 java 并在第二个数组中找到 postgresql,这并不意味着没有 java。我们现在只需要忽略它。如果我们没有找到匹配项,那么我们将分配 nil-s

    h2 = a2.find { |h| h1["name"] == h["name"] } || {}
    

    现在我们要么找到匹配项,要么没有找到匹配项,我们可以像你一样将结果添加到组合数组中。

    combined << {"name"=> h1["name"], "version"=> h1["version"], "release"=> h2["release"], "url"=> h2["url"]}
    

    大家一起:

    a1= [{"name"=>"postgresql", "version"=>29}, {"name"=>"java", "version"=>8}, {"name"=>"python", "version"=>3}]
    a2= [{"name"=>"postgresql", "release"=>"postgresql-release", "url"=>"https://github.com/repo/postgresql-release"}, {"name"=>"java", "release"=>"java-release", "url"=>"https://github.com/repo/java-release"}]
    
    combined = []
    
    a1.each do |h1|
      h2 = a2.find { |h| h1["name"] == h["name"] } || {}
      combined << {"name"=> h1["name"], "version"=> h1["version"], "release"=> h2["release"], "url"=> h2["url"]} 
    end
    puts combined.to_s #=> [{"name"=>"postgresql", "version"=>29, "release"=>"postgresql-release", "url"=>"https://github.com/repo/postgresql-release"}, {"name"=>"java", "version"=>8, "release"=>"java-release", "url"=>"https://github.com/repo/java-release"}, {"name"=>"python", "version"=>3, "release"=>nil, "url"=>nil}]
    

    更简单的方法是使用map。然后你可以避免额外的数组和手动附加

    a1= [{"name"=>"postgresql", "version"=>29}, {"name"=>"java", "version"=>8}, {"name"=>"python", "version"=>3}]
    a2= [{"name"=>"postgresql", "release"=>"postgresql-release", "url"=>"https://github.com/repo/postgresql-release"}, {"name"=>"java", "release"=>"java-release", "url"=>"https://github.com/repo/java-release"}]
    
    combined = a1.map do |h1|
      h2 = a2.find { |h| h1["name"] == h["name"] } || {}
      {"name"=> h1["name"], "version"=> h1["version"], "release"=> h2["release"], "url"=> h2["url"]} 
    end
    puts combined.to_s #=> [{"name"=>"postgresql", "version"=>29, "release"=>"postgresql-release", "url"=>"https://github.com/repo/postgresql-release"}, {"name"=>"java", "version"=>8, "release"=>"java-release", "url"=>"https://github.com/repo/java-release"}, {"name"=>"python", "version"=>3, "release"=>nil, "url"=>nil}]
    

    另一个可能的改进是使用合并。

    combined = a1.map do |h1|
      h2 = a2.find { |h| h1["name"] == h["name"] } || { "release" => nil, "url" => nil}
      h1.merge(h2)
    end
    

    【讨论】:

    • 效果很好!我已经实现了第一个变体,因为它目前对我来说是最容易理解的,因为缺乏 Ruby 知识! :) 我有一个关于这部分的问题:h2 = a2.find { |h| h1["name"] == h["name"] } || {} 我不知道到底发生了什么,你能再一步一步地向我解释一下吗?非常感谢您的帮助^^
    • 当然。 find 将开始遍历数组并运行块,直到找到块返回true(或任何真实的东西)的第一个块。然后返回。如果它什么也没找到并到达终点,则返回nil。因此,我正在遍历第二个数组以寻找具有相同名称的哈希。如果我找到它,很好。如果我不这样做,我默认为空散列 (|| {}),因为在空散列上调用 h2["release"] 默认返回 nil。通过让 h2 始终是哈希值,这使得以下代码更简单。那我以后就不用检查它是否存在了。
    • 很好的解释!!!我现在明白了,提到的文档帮助更大。非常感谢!! :)
    【解决方案2】:

    你可以试试这个代码:

    combined = h1
      .concat(h2)
      .group_by { |a| a['name'] }
      .values
      .map do |a|
        res = a.first.merge(a.last)
        res['release'] ||= nil
        res['url'] ||= nil
        res
      end
    

    如果您不确定代码在做什么,请尝试逐步执行。

    【讨论】:

    • 它按预期工作,非常感谢您的帮助!
    • 我的荣幸。不要犹豫,验证我的答案:)
    • 当然我已经完成了,但我还需要 15 点声望才能让其他人看到。一旦我得到它们,我的验证就会可见!
    • 哦,好的 ^^ 谢谢 :)
    【解决方案3】:

    可能有很多方法可以做到这一点,但是由于您的内部哈希本质上具有一个键,因此首先想到的是将您的哈希数组转换为哈希的哈希,然后只需使用 Hash#deep_merge 即可正是这种东西。

    hash1 = h1.index_by { |inner_hash| inner_hash['name'] }
    hash2 = h2.index_by { |inner_hash| inner_hash['name'] }
    combined_hash = hash1.deep_merge(hash2)
    combined = combined_hash.values
    

    这给出了除了 python nil URL 之外的预期输出。如果您无法在数据中修复该问题,要复制该结果,请尝试以下操作:

    # ... as before
    defaults = {'url' => nil, 'version' => nil}
    combined_hash = combined_hash.transform_values { |v| defaults.merge(v) }
    combined = combined_hash.values
    

    如果您需要将默认值作为可能更改的数据中的确切键:

    keys = (hash1.keys + hash2.keys).uniq
    defaults = keys.map { |k| [k, nil] }.to_h
    

    【讨论】:

    • 您可以使用index_by,而不是第一个map-s。就是这个意思:h1.index_by { |h| h['name'] }
    猜你喜欢
    • 2016-02-27
    • 1970-01-01
    • 1970-01-01
    • 2016-08-19
    • 2018-06-28
    • 1970-01-01
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    相关资源
    最近更新 更多