Following the comments of Akira and others, here's a revised proposal
merging his original Enumerable#categorize with my previous version.

Like Akira's categorize, it now:
* can produce nested hashes
* returns an Enumerator with not given a block

Like my original proposal, it still:
* has a simple interface with a single argument for special merges
* does not produce "grouped hashes" by default

Here is what the documentation could read like:

    enum.associate(merge =3D nil){ block } # =3D> a_hash

    Invokes block once for each element of +enum+. Creates a new hash based=
 on
    the values returned by the block. These values are interpreted as a seq=
uence
    of keys and the final value.

       (1..3).associate {|e| ["#{e} + #{e}", e+e] }
         #=3D> {"1 + 1" =3D> 2, "2 + 2" =3D> 4, "3 + 3" =3D> 6}

    If more than one key is specified, the resulting hash will be nested.

       (0..7).associate {|e| [e&4, e&2, e&1, e] }
         #=3D> {0=3D>{0=3D>{0=3D>0,
                     1=3D>1},
                 2=3D>{0=3D>2,
                     1=3D>3}},
             4=3D>{0=3D>{0=3D>4,
                     1=3D>5},
                 2=3D>{0=3D>6,
                     1=3D>7}}}

    If no key is specified, either because the block returned an array
    with less than two elements, or because only the value is not an Array,
    then the key is assumed to be the yielded element itself
    (or the first element in case many elements are yielded):

       (1..4).associate{|i| i ** i} # =3D> {1 =3D> 1, 2 =3D> 2, 3 =3D> 27, =
4 =3D> 256}
       {:foo =3D> 2, :bar =3D> 3}.associate{|k, v| v ** v}
         # =3D> {:foo =3D> 4, :bar =3D> 9}

    In case of key duplication, +merge+ will be used. If +nil+, the value
    is overwritten. Otherwise the stored value will be the result of callin=
g
    `merge` with the arguments +key+, +first_value+ and +other_value+
    (see Hash#merge). In a similar way to `Enumerable#inject`, passing a sy=
mbol
    for +merge+ is equivalent to passing
    <tt>->(key, first, other){ first.send(merge, other) }</tt>

       x =3D [[:foo, 10], [:bar, 30], [:foo, 32]]
       x.associate{|e| e}                    # =3D> {:foo =3D> 32, :bar =3D=
> 30}
       x.associate(->(k, a, b){a}){|e| e}    # =3D> {:foo =3D> 10, :bar =3D=
> 30}
       x.associate(:+){|e| e}                # =3D> {:foo =3D> 42, :bar =3D=
> 30}
       x.associate(:concat){|k, v| [k, [v]]} # =3D> {:foo =3D> [10, 32],
:bar =3D> [30]}


A question that remains is: should there be special checks for cases
where the result has varying length?
E.g., what error should the following raise (or what should be the result):

    [[:foo, :bar], [:foo, :bar, :baz]].associate{|x| x}  # =3D> ??

Here is what a Ruby implementation could look like:

 module Enumerable
   def associate(merge_func =3D nil)
     return to_enum, __method__, merge_func unless block_given?

     if merge_func.is_a? Symbol
       sym =3D merge_func
       merge_func =3D ->(k, v1, v2){v1.send(sym, v2)}
     end

     top_level_hash =3D {}
     each do |*elems|
       result =3D yield(*elems)
       result =3D [result] unless result.is_a? Array
       value =3D result.pop

       if result.empty? # deduce key
         key =3D elems.first
         key =3D key.first if key.is_a?(Array)
         initial_keys =3D []
       else
         key =3D result.pop
         initial_keys =3D result
       end

       final_hash =3D initial_keys.inject(top_level_hash){|cur_h, k|
cur_h[k] ||=3D {}}

       if merge_func && final_hash.has_key?(key)
         value =3D merge_func.call(key, final_hash[key], value)
       end
       final_hash[key] =3D value
     end
     top_level_hash
   end
 end


Thanks
--
Marc-Andr=E9