How can I extract the bigger value for each key in

2019-05-10 13:08发布

问题:

I can imagine there is a simple way to do that instead of use many variables and state.

I just want to get the highest value given for each key in a list of hashes

For example:

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

回答1:

I'd 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}]


回答2:

This is a variant of @Matt's answer:

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


回答3:

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's the fruity benchmark comparison:

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

Output:

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%


回答4:

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}


回答5:

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 Cary Swoveland for group_by(&:keys).