[#35446] [Ruby 1.9 - Bug #4477][Open] Kernel:exec and backtick (`) don't work for certain system commands — Joachim Wuttke <j.wuttke@...>

10 messages 2011/03/07

[#35476] [Ruby 1.9 - Bug #4489][Open] [PATCH] Encodings with /-(unix|dos|mac)\Z/ — "James M. Lawrence" <quixoticsycophant@...>

20 messages 2011/03/10

[#35552] [Ruby 1.9 - Feature #4523][Open] Kernel#require to return the path of the loaded file — Alex Young <alex@...>

14 messages 2011/03/24

[#35565] [Ruby 1.9 - Feature #4531][Open] [PATCH 0/7] use poll() instead of select() in certain cases — Eric Wong <normalperson@...>

33 messages 2011/03/28

[#35566] [Ruby 1.9 - Feature #4532][Open] [PATCH] add IO#pread and IO#pwrite methods — Eric Wong <normalperson@...>

12 messages 2011/03/28

[#35586] [Ruby 1.9 - Feature #4538][Open] [PATCH (cleanup)] avoid unnecessary select() calls before doing I/O — Eric Wong <normalperson@...>

9 messages 2011/03/29

[ruby-core:35563] Re: [feature:trunk] Enumerable#categorize

From: Marc-Andre Lafortune <ruby-core-mailing-list@...>
Date: 2011-03-27 03:25:07 UTC
List: ruby-core #35563
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 = nil){ block } # => 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 sequence
    of keys and the final value.

       (1..3).associate {|e| ["#{e} + #{e}", e+e] }
         #=> {"1 + 1" => 2, "2 + 2" => 4, "3 + 3" => 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] }
         #=> {0=>{0=>{0=>0,
                     1=>1},
                 2=>{0=>2,
                     1=>3}},
             4=>{0=>{0=>4,
                     1=>5},
                 2=>{0=>6,
                     1=>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} # => {1 => 1, 2 => 2, 3 => 27, 4 => 256}
       {:foo => 2, :bar => 3}.associate{|k, v| v ** v}
         # => {:foo => 4, :bar => 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 calling
    `merge` with the arguments +key+, +first_value+ and +other_value+
    (see Hash#merge). In a similar way to `Enumerable#inject`, passing a symbol
    for +merge+ is equivalent to passing
    <tt>->(key, first, other){ first.send(merge, other) }</tt>

       x = [[:foo, 10], [:bar, 30], [:foo, 32]]
       x.associate{|e| e}                    # => {:foo => 32, :bar => 30}
       x.associate(->(k, a, b){a}){|e| e}    # => {:foo => 10, :bar => 30}
       x.associate(:+){|e| e}                # => {:foo => 42, :bar => 30}
       x.associate(:concat){|k, v| [k, [v]]} # => {:foo => [10, 32],
:bar => [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}  # => ??

Here is what a Ruby implementation could look like:

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

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

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

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

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

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


Thanks
--
Marc-Andr

In This Thread