Daniel Martin wrote:
Why build up a separate hash structure?
It's simpler to my eyes. It's also (admittedly unintuitively) faster
though.
Here's a quick test with your version, my original, and another going
off your idea, but I tried to simplify it a bit:
#!/opt/local/bin/ruby
class Symbol
def to_proc
lambda { |x| x.send(self) }
end
end
module Enumerable
def max_by1
last_value = nil
inject do |max_item, item|
current_value = yield item
if last_value.nil? || current_value > last_value
last_value = current_value
item
else
max_item
end
end
end
def max_by2
pairs = inject({}) do |hash, obj|
hash[yield obj] = obj; hash
end
pairs[pairs.keys.max]
end
def max_by3
max_value = nil
max_item = nil
each do |item|
value = yield(item)
max_item, max_value = item, value if max_value.nil? || value > max_value
end
max_item
end
end
a = %w{ one two three four }
16.times do |i|
a = a + a
end
puts "a.size => #{a.size}"
require 'benchmark'
Benchmark::bm do |bm|
bm.report { puts a.max_by1 &:size }
bm.report { puts a.max_by2 &:size }
bm.report { puts a.max_by3 &:size }
end
I couldn't tell you why the 3rd is so much faster than the first. Just
goes to prove the old programming adage though: "Optimizing before
Profiling is a waste of time."
Not that this counts as serious profiling of course. 