[Note: parts of this message were removed to make it a legal post.]
Well... inject ALWAYS loses, but fanboys sure seems to like it for no good
reason.
By using better names and the right tool for the job, this becomes a LOT
more readable, maintanable, and faster all in one fell swoop:
by_length = Hash.new { |h,k| h[k] = [] }
strings.each do |string|
by_length[string.length] << string
end
by_length.values # I think this part is a mistake, but I wanted to match
I think the readability is more important than speed by a long shot... But
just in case you're not convinced, check out the benchmarks:
% ./blah.rb 10000
# of iterations = 10000
user system total real
null_time 0.000000 0.000000 0.000000 ( 0.001370)
mine 7.790000 0.050000 7.840000 ( 7.869737)
yours-inject 15.170000 0.050000 15.220000 ( 15.554334)
yours-each 11.850000 0.100000 11.950000 ( 12.013553)
inject is twice as slow as mine. stop using it.
I generalized yours, and made the returned groups sorted by the results from
the call. In this more comparable situation, inject is about 11% slower, not
twice as slow.
Inject Test
Rehearsal --------------------------------------------------
Without Inject 14.160000 0.100000 14.260000 ( 14.364824)
With Inject 15.950000 0.120000 16.070000 ( 16.258609)
---------------------------------------- total: 30.330000sec
user system total real
Without Inject 14.200000 0.110000 14.310000 ( 14.553592)
With Inject 16.000000 0.120000 16.120000 ( 16.422186)
Inject is about 11.38% slower
Here is the code:
#!/usr/bin/env ruby
require 'benchmark'
class Symbol
def to_proc
Proc.new{|obj| obj.send self } # give 1.9ish syntax
end
end
module Enumerable
def group_by_without_inject( &get_key )
groups = Hash.new { |h,k| h[k] = Array.new }
each do |obj|
groups[ get_key[obj] ] << obj
end
groups.keys.sort!.map!{|key| groups[key] }
end
def group_by_with_inject( &get_key )
groups = inject Hash.new{ |h,k| h[k] = Array.new } do |groups,obj|
groups[ get_key[obj] ] << obj
groups
end
groups.keys.sort!.map!{|key| groups[key] }
end
end
puts "Inject Test"
benchmarks = Benchmark.bmbm do|b|
x = ["abc","abcde","def","xyzwj"]
b.report("Without Inject") do
500_000.times{ x.group_by_without_inject &:length }
end
b.report("With Inject") do
500_000.times{ x.group_by_with_inject &:length }
end
end
benchmarks.map!{|b| b.real }
percent_slower = sprintf( "%.2f" , 100 - 100 * benchmarks.first /
benchmarks.last )
puts '' , "Inject is about #{ percent_slower }% slower"