可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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)
.