tap vs. each_with_object: tap is faster and less typing.
Ruby 1.9 introduced Enumerable#each_with_object, a crazy-specific method that takes an object, invokes each while also yielding that object, and then returns the object. For example:
by_id = items.each_with_object({}){ |item,h| h[item.id] = item }
It’s almost exactly the same as good old Enumerable#inject, except that you don’t have to ensure that the memo object is the last expression in the block:
by_id = items.inject({}){ |h,item| h[item.id] = item; h }
Ruby 1.9 also introduced Object#tap, a general-purpose method that yields the receiver to the block and returns it when done:
by_id = {}.tap{ |h| items.each{ |item| h[item.id] = item } }
I don’t really understand people who use each_with_object. Using tap/each is always fewer characters to type. It uses general-purpose methods instead of a special-case method whose yielded-parameter order you have to remember. (It’s the opposite of the order for inject.) And as an added bonus, it’s also always slightly faster:
N = 1_000_000 nums = N.times.map{ rand(N) } # Lots of random numbers require 'benchmark' Benchmark.bmbm do |x| x.report('inject'){ nums.inject({}){ |h,n| h[n]=n; h } } x.report('tap/each'){ {}.tap{ |h| nums.each{ |n| h[n]=n } } } x.report('ea_wi_obj'){ nums.each_with_object({}){ |n,h| h[n]=n } } end #=> user system total real #=> inject 0.660000 0.020000 0.680000 ( 0.682896) #=> tap/each 0.630000 0.010000 0.640000 ( 0.636919) #=> ea_wi_obj 0.950000 0.030000 0.980000 ( 0.971507)
|
Michael Kohl
05:18PM ET 2012-Feb-23 |
I think the order of block arguments make sense, it’s consistent with My problem with |