How do I summarize hashes within a hash?

hashmap, ruby

Solution

First and foremost don't use upper-case letters for variables in Ruby as you did with `SomeHash` and `XPath`. When a variable name starts with an upper-case letter, it means it's a constant and you probably didn't want it to be a constant.

`each` is not the best way to do this, you can do this much more simply with `inject` as in:

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

The output for this is:

{"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"]}

You have all countries grouped by continent and you can pick any one of them.

In your code it should be placed like this:

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

Problem

I have a hash like this: ``` 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",} ``` I would like to identify the five continents individually, and the countries that belong to them, such that I end up with something like this: ``` {"Africa" => ["Senegal", "Somalia"]} {"Europe" => ["Albania", "Andorra", "Austria"]} ``` for all of the continents. I tried this: ``` 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 ``` and it works much too well. I get a new hash: ``` {Africa => []} ``` but I have two problems: - I create a new hash for each African country. - Each new hash contains all the keys, which includes all non-African countries. I think the first problem has to do with the `each` method so I have to set some conditional, right? The second problem, I have no idea how to fix. Any pointers would be greater appreciated.

Original source