Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I summarize hashes within a hash?

Tags:

hashmap

ruby

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:

  1. I create a new hash for each African country.
  2. 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.

like image 991
Uzzar Avatar asked Oct 03 '22 05:10

Uzzar


1 Answers

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
like image 54
Maurício Linhares Avatar answered Oct 25 '22 20:10

Maurício Linhares