[#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:35564] Re: [feature:trunk] Enumerable#categorize

From: Michael Edgar <adgar@...>
Date: 2011-03-27 19:50:21 UTC
List: ruby-core #35564
This looks astounding. Quick nit: is it #categorize or #associate? I like #categorize
as a name for this more, but you've given code samples with #associate as the working
title of the method.

I really like what you've presented here.

I have one idea for how to handle varying key lengths: I think I'd like an option to (or the
default case to be, even, but at least an option) have a replacing, mixed-mode result: both
values and mixed nesting is allowed, and you can replace a value with a nesting level
if key duplication occurs. Example where the categories are fruits:

[  [:aaa, "plum"],
   [:aaa, :bbb, "banana"],
   [:aaa, :ccc, "lemon"],
   [:foo, :bar, "pear"],
   [:foo, "apple"],
   [:zzz, "orange" ]  ].categorize { |a| a }

should give:

 {:aaa => {:bbb => "banana", :ccc => "lemon" },
  :foo => "apple" },
  :zzz => "orange" }

It's a neat way to provide a useful result, as well as to avoid the overhead of having to
actually check for this... though, the overhead is asymptotically negligible.

Just food for thought.

Cheers,
Michael Edgar
adgar@carboni.ca
http://carboni.ca/

On Mar 26, 2011, at 11:25 PM, Marc-Andre Lafortune wrote:

> 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

Prev Next