如何在哈希中汇总哈希?

时间:2013-12-08 03:31:16

标签: ruby hashmap

我有这样的哈希:

Some_hash =
    {"Albania"=>"Europe", 
    "Andorra"=>"Europe", 
    "Austria"=>"Europe",
    Lebanon"=>"Asia", 
    "Macau"=>"Asia", 
    "Malaysia"=>"Asia",
    "Papua New Guinea"=>"Asia",
    "Jamaica"=>"North America",
    "Martinique"=>"North America",
    "Argentina"=>"South America",
    "Chile"=>"South America", 
    "Sao Tome and Principe"=>"Africa", 
    "Senegal"=>"Africa",
    "Somalia"=>"Africa",}

我想单独确定五大洲以及属于他们的国家,这样我最终会得到这样的结论:

{"Africa" => ["Senegal", "Somalia"]}
{"Europe" => ["Albania", "Andorra", "Austria"]}

适用于所有大陆。

我试过了:

def country
  inflation_hash = {}
  XPath.match( data, "//country").map do |element|
    inflation_hash[element.attributes["name"]] = element.attributes["continent"]
  end
  inflation_hash.each do |country, continent|
    new_hash = {}
    if inflation_hash.has_value?("Africa") == true
      new_hash["Africa"] = inflation_hash.keys
      puts new_hash
    end
  end
end

它的效果非常好。我得到一个新的哈希:

{Africa => []} 

但我有两个问题:

  1. 我为每个非洲国家创建了一个新哈希。
  2. 每个新哈希都包含所有密钥,其中包括所有非非洲国家。
  3. 我认为第一个问题与each方法有关,所以我必须设置一些条件,对吧?

    第二个问题,我不知道如何修复。

    任何指针都会更受欢迎。

5 个答案:

答案 0 :(得分:3)

首先,不要像使用SomeHashXPath那样在Ruby中使用大写字母表示变量。当变量名以大写字母开头时,它意味着它是一个常数,你可能不希望它是一个常数。

each不是最好的方法,您可以使用inject更简单地执行此操作,如下所示:

countries = {
    "Albania"=>"Europe", 
    "Andorra"=>"Europe", 
    "Austria"=>"Europe",
    "Lebanon"=>"Asia", 
    "Macau"=>"Asia", 
    "Malaysia"=>"Asia",
    "Papua New Guinea"=>"Asia",
    "Jamaica"=>"North America",
    "Martinique"=>"North America",
    "Argentina"=>"South America",
    "Chile"=>"South America", 
    "Sao Tome and Principe"=>"Africa", 
    "Senegal"=>"Africa",
    "Somalia"=>"Africa"}

by_continents = countries.inject({}) do |memo, (k,v)|
  memo[v] ||= []
  memo[v] << k
  memo
end

这个输出是:

{"Europe"=>["Albania", "Andorra", "Austria"], "Asia"=>["Lebanon", "Macau", "Malaysia", "Papua New Guinea"], "North America"=>["Jamaica", "Martinique"], "South America"=>["Argentina", "Chile"], "Africa"=>["Sao Tome and Principe", "Senegal", "Somalia"]}

您所有国家/地区都按大陆分组,您可以选择其中任何一个。

在您的代码中,它应该像这样放置:

def country
  inflation_hash = {}
  XPath.match( data, "//country").map do |element|
    inflation_hash[element.attributes["name"]] = element.attributes["continent"]
  end
  by_continents = inflation_hash.inject({}) do |memo, (k,v)|
    memo[v] ||= []
    memo[v] << k
    memo
  end
  puts by_continents.inspect
  by_continents
end

答案 1 :(得分:1)

以下是我解决问题的方法:


def sort_by_continents
  # Initialize example Hash of countries:
  country_map = {"Albania"=>"Europe", "Andorra"=>"Europe",
                 "Lebanon"=>"Asia", "Macau"=>"Asia",
                 "Jamaica"=>"North America", "Chile"=>"South America",
                 "Senegal"=>"Africa", "Malaysia"=>"Asia"}

  # Create a new Hash where initial values are = []
  continent_map = Hash.new{|h,k| h[k] = []}

  # For each country in the initial hash:
  #   Add the corresponding country to the appropriate continent.
  country_map.each {|country,continent| continent_map[continent] << country}

  # Return the continent map.
  continent_map
end

记忆肯定是最好和最有效的方法(如上所述),但对于初学者,我说从一些有意义的事情开始。一旦你花了更多时间在Ruby上,就会有记忆和优化 - 我知道当我开始时,inject||=的概念令人难以置信。从基础开始始终是最好的方法。

祝你好运,希望这会有所帮助!

答案 2 :(得分:0)

您可以在构建inflation_hash的同时执行此操作,只需使用default_proc on the Hash to auto-vivify个新元素作为空数组:

inflation_hash = { }
new_hash       = Hash.new { |h, k| h[k] = [ ] }
XPath.match(data, "//country").map do |element|
  name, continent = element.attributes.values_at('name', 'continent')
  inflation_hash[name] = continent
  new_hash[continent].push(name)
end

这将使您获得inflation_hash,因为您现在拥有它并new_hash喜欢:

{
  "Africa" => ["Senegal", "Somalia"],
  "Europe" => ["Albania", "Andorra", "Austria"],
  ...
}

答案 3 :(得分:0)

你已经有了哈希,所以为什么不做你需要的地方:

countries.keys.each { |k| 
  (countries[countries.delete(k)] ||= []) << k
}

如果您的国家/地区的名称与大陆完全相同,则会失败,但对您来说不是这样,对吗?

答案 4 :(得分:0)

我会考虑两种方式:

version1 = countries.each_with_object({}) do |(key,value),result|
 (result[value] ||= []) << key
end

version2 = Hash[countries.group_by(&:last).map{|x,y|[x,y.map(&:first)]}]