How can I extract a larger value for each key in a list of hashes in Ruby

I can imagine that there is an easy way to do this instead of using many variables and state.

I just want to get the maximum value specified for each key in the hash list

For instance:

[{1=>19.4}, {1=>12.4}, {2=>29.4}, {3=>12.4}, {2=>39.4}, {2=>59.4}]

Result

[{1=>19.4}, {3=>12.4}, {2=>59.4}]
+3
source share
5 answers

I would do as below:

a = [{1=>19.4}, {1=>12.4}, {2=>29.4}, {3=>12.4}, {2=>39.4}, {2=>59.4}]

# the below is the main trick, to group the hashes and sorted the key/value pair
# in **ascending order**.
a.sort_by(&:to_a)
# => [{1=>12.4}, {1=>19.4}, {2=>29.4}, {2=>39.4}, {2=>59.4}, {3=>12.4}]

# then using the trick in mind, we know hash can't have duplicate keys, so
# blindly I can rely on `Enumerable#inject` with the `Hash#merge` method.
a.sort_by(&:to_a).inject(:merge)
# => {1=>19.4, 2=>59.4, 3=>12.4}

# final one
a.sort_by(&:to_a).inject(:merge).map { |k,v| {k => v} }
# => [{1=>19.4}, {2=>59.4}, {3=>12.4}]
+5
source

This is a @Matt answer option:

 a.group_by(&:keys).map {|k,v| {k.first => v.map(&:values).flatten.max}}
   #=> [{1=>19.4}, {2=>59.4}, {3=>12.4}]
+3
source

How about this?

Hash[a.flat_map(&:to_a).sort_by(&:last)]
# a.flat_map(&:to_a).sort_by(&:last).to_h for Ruby 2.1+ as @steenslag suggested
=> {3=>12.4, 1=>19.4, 2=>59.4}

Here is a comparative comparison fruity:

require 'fruity'
a = 1000.times.collect { |i| { rand(100) => rand(1000) } }

compare do
  caryswoveland { 
   a.group_by(&:keys).map {|k,v| {k.first => v.map(&:values).flatten.max}} 
  }
  matt { a.group_by(&:keys).map { |k,v| v.max_by { |j| j[k[0]] } } }
  steenslag { 
    a.each_with_object({}){|h, res| 
      res.merge!(h){|k, *vals| res[k] = vals.max} 
    } 
  }
  abdo { Hash[a.flat_map(&:to_a).sort_by(&:last)] }
end

Conclusion:

Running each test 4 times. Test will take about 1 second.
abdo is similar to steenslag (results differ..)
steenslag is faster than matt by 30.000000000000004% ± 10.0%
matt is faster than caryswoveland by 30.000000000000004% ± 10.0%
+3
source
a = [{1=>19.4}, {1=>12.4}, {2=>29.4}, {3=>12.4}, {2=>39.4}, {2=>59.4}]

a.group_by(&:keys).map { |k,v| v.max_by { |j| j[k[0]] } }

[{1 => 19.4}, {2 => 59.4}, {3 => 12.4}]

Credit to Carey Swoveland for group_by(&:keys).

+1
source
a = [{1=>19.4}, {1=>12.4}, {2=>29.4}, {3=>12.4}, {2=>39.4}, {2=>59.4}]

p a.each_with_object({}){|h, res| res.merge!(h){|k, *vals| res[k] = vals.max} }
#=> {1=>19.4, 2=>59.4, 3=>12.4}
+1
source

All Articles