With a bit of Higher order programming:
myarray.reduce({}) do |accu, (country, func, name)|
accu[country] ||= {}
accu[country][func.to_sym] = name
accu
end.map{|k, h| h[:country] = k; h}
Explanation:
Reduce will take an accumulator, in this case we start with an empty hash and go through the array. We match the triples in the array to the variables country, func, name
. Since we want to group things by country we create that as our first Hash key. It should contain a hash so we make sure that is corresponds to an array with accu[country] ||= {}
.
Then we add our key value pair converting func
to a symbol. Finally we return our modified accumulator that will be passed to the next iteration.
This will return a data structure like this:
{"usa"=>{:primary=>"john", :secondary=>"steve"},
"france"=>{:primary=>"lira", :secondary=>"ben"},
"germany"=>{:primary=>"jeff"}}
Now we need to transform it into a array of hashes rather than a big hash. We do this by calling map
on it and in the process we add country
as a key to the hash.
Now one thing that the algorithm above doesn't do is to check for missing values, resp. it doesn't guarantee that both :primary
and :secondary
are present. You can do that by modifying the map
to this:
.map do |k, h|
h[:country] = k
h[:primary] ||= ""
h[:secondary] ||= ""
h
end