I saw this while browsing the Merb source code, and despite the bad variable names thought it was pretty nice:
module Enumerable
def injecting(s)
inject(s) do |k, i|
yield(k, i); k
end
end
end
Use like:
['o', 'hai', 'rly', 'srsly'].injecting({}) {|accumulator, value| accumulator[value] = value.size}
# => {"srsly"=>5, "o"=>1, "hai"=>3, "rly"=>3}
Simple pleasures. I find it increasingly hard to live without things like this, Object#returning, and Symbol#to_proc as I start to use them. After all…
Sorry, comments are closed for this article.
August 3rd, 2007 at 05:45 AM
I’ll never like #returning, its more to type and slower to execute than the regular way.
August 3rd, 2007 at 06:23 AM
Just to keep from forgetting to make the accumulator the value of the block? {{{ %w[ o hai rly srsly ].inject({}) {|accumulator,value| accumulator[value]=value.size; accumulator} }}} I’d typically have written the block parameters as |h,e| h for hash, e for element. Heck, I might even spell out element or use hsh over just h. I just don’t see where this gives you such a warm fuzzy.
Maybe you’d like something such as this: {{{ module Enumerable # Like each_with_index, but the value is the array of block results. def map_with_index a = [] each_with_index { |e,i| a << yield(e, i) } a end end }}}
August 3rd, 2007 at 10:20 AM
“Just to keep from forgetting to make the accumulator the value of the block?” Well, yes. That’s the point of abstraction, that it allows you to hide “all that tedious mucking about in hyperspace”, which then allows you to extend the idea further. Look how much we’ve got out of abstracting 2 dimensional vectors as complex numbers. See also:
http://weblog.raganwald.com/2007/07/abbreviation-accidental-complexity-and.html
I suspect that supplied parameter should be optional, however, as it is for inject.
August 3rd, 2007 at 02:39 PM
Rob, yea that’s it. Like I said it’s a simple pleasure. But I use #inject a lot and my brain, for some reason, rarely remembers to return the accumulator from the block, even though I always work myself up before using it to remember. So even though it’s a trivially small problem with an easy solution, it causes dissonance and a minor sense of dissatisfaction. A break in flow. I shouldn’t feel like that when I use Ruby. And, with simple techniques like this, I don’t have to.
August 3rd, 2007 at 02:48 PM
Where is the ‘10’ coming from? I don’t see it in the accumulator or the value. The method itself returns only the hash. If that’s supposed to be the total of the hash values, it ought to be 12.
August 3rd, 2007 at 04:43 PM
Daniel, bad paste. I removed it (thanks).
August 9th, 2007 at 05:41 PM
runtime ratio between equivalent each:inject:injectings :: 5.5:8.8:13.2. Flog ratio is 9.1:11.7:18.7.
Knowing this, and knowing just how clean the #each version reads, why would you EVER use something only marginally more readable (and that is debatable, see below) and 2.4x slower?
hash = {} do |str| hash[str] = str.size end
vs.
hash = [‘o’, ‘hai’, ‘rly’, ‘srsly’].inject({}) {|accumulator, str| accumulator[str] = str.size accumulator }
vs.
hash = [‘o’, ‘hai’, ‘rly’, ‘srsly’].injecting({}) {|accumulator, str| accumulator[str] = str.size }
August 12th, 2007 at 02:25 AM
Performance considerations aside, here’s my preferred version. Bit less flexible, but seems like the idea is generally to convert to a hash anyway:
module Enumerable #[‘o’, ‘hai’, ‘rly’, ‘srsly’].map_to_hash {|word| {word => word.length} } def map_to_hash(s = {}) inject(s) do |k, i| key, val = *yield(i).to_a.flatten k[key] = val k end end end
August 20th, 2007 at 03:59 PM
Damn – this made me think to take a peek into Rails’ Enumerable extensions. Should’ve known that it would have these.. http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/core_ext/enumerable.rb
August 25th, 2007 at 03:03 PM
Somewhat off topic, but on XP install of Ruby 1.8.6, if I have a line break in the source file at the “{|accumulator….”, I get a syntax error from Ruby.
If I leave it all as one line or break the line after the “{” ({|accumulator, ..) it works..
I thought Ruby was “free form” for it’s source code formatting ?
Mike
(attempt to use a
tag below, don't know if it will work on this blog or not ) <pre> ['o', 'hai', 'rly', 'srsly'].injecting({}) {|accumulator, value| accumulator[value] = value.size ; print "\n value = #{value} accumulator = #{accumulator} accumulator[value] = #{accumulator[value]}\n"} </pre>August 29th, 2007 at 04:35 PM
Mike, it’s proper behavior. In first line Ruby interpreter see a method call ( .injecting({}) ) and don’t look forward (there is no reason to do so). The second line cause syntax error (it looks like hash but it’s not).