Contact Me
The Passionate Programmer: Creating a Remarkable Career in Software Development
Rails Recipes

Enumerable#injecting

August 3rd, 2007

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…

11 Comments

  1. Eric Hodel Says:

    I’ll never like #returning, its more to type and slower to execute than the regular way.

  2. Rob Biedenharn Says:

    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 }}}

  3. hgs Says:

    “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.

  4. Chad Fowler Says:

    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.

  5. Daniel Berger Says:

    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.

  6. Chad Fowler Says:

    Daniel, bad paste. I removed it (thanks).

  7. Ryan Davis Says:

    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 }

  8. crayz Says:

    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

  9. csy Says:

    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

  10. Mike Stramba Says:

    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>

  11. Radarek Says:

    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).

Sorry, comments are closed for this article.