[#33640] [Ruby 1.9-Bug#4136][Open] Enumerable#reject should not inherit the receiver's instance variables — Hiro Asari <redmine@...>

Bug #4136: Enumerable#reject should not inherit the receiver's instance variables

10 messages 2010/12/08

[#33667] [Ruby 1.9-Bug#4149][Open] Documentation submission: syslog standard library — mathew murphy <redmine@...>

Bug #4149: Documentation submission: syslog standard library

11 messages 2010/12/10

[#33683] [feature:trunk] Enumerable#categorize — Tanaka Akira <akr@...>

Hi.

14 messages 2010/12/12
[#33684] Re: [feature:trunk] Enumerable#categorize — "Martin J. Dst" <duerst@...> 2010/12/12

[#33687] Towards a standardized AST for Ruby code — Magnus Holm <judofyr@...>

Hey folks,

23 messages 2010/12/12
[#33688] Re: Towards a standardized AST for Ruby code — Charles Oliver Nutter <headius@...> 2010/12/12

On Sun, Dec 12, 2010 at 9:55 AM, Magnus Holm <judofyr@gmail.com> wrote:

[#33689] Re: Towards a standardized AST for Ruby code — "Haase, Konstantin" <Konstantin.Haase@...> 2010/12/12

On Dec 12, 2010, at 17:46 , Charles Oliver Nutter wrote:

[#33763] [Ruby 1.9-Bug#4168][Open] WeakRef is unsafe to use in Ruby 1.9 — Brian Durand <redmine@...>

Bug #4168: WeakRef is unsafe to use in Ruby 1.9

43 messages 2010/12/17

[#33815] trunk warnflags build issue with curb 0.7.9? — Jon <jon.forums@...>

As this may turn out to be a 3rd party issue rather than a bug, I'd like some feedback.

11 messages 2010/12/22

[#33833] Ruby 1.9.2 is going to be released — "Yuki Sonoda (Yugui)" <yugui@...>

-----BEGIN PGP SIGNED MESSAGE-----

15 messages 2010/12/23

[#33846] [Ruby 1.9-Feature#4197][Open] Improvement of the benchmark library — Benoit Daloze <redmine@...>

Feature #4197: Improvement of the benchmark library

15 messages 2010/12/23

[#33910] [Ruby 1.9-Feature#4211][Open] Converting the Ruby and C API documentation to YARD syntax — Loren Segal <redmine@...>

Feature #4211: Converting the Ruby and C API documentation to YARD syntax

10 messages 2010/12/26

[#33923] [Ruby 1.9-Bug#4214][Open] Fiddle::WINDOWS == false on Windows — Jon Forums <redmine@...>

Bug #4214: Fiddle::WINDOWS == false on Windows

15 messages 2010/12/27

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

From: Marc-Andre Lafortune <ruby-core-mailing-list@...>
Date: 2010-12-27 07:02:44 UTC
List: ruby-core #33921
Hi!

On Sat, Dec 25, 2010 at 3:45 AM, Tanaka Akira <akr@fsij.org> wrote:
> I feel many people needs hash creation.

I fully agree.

This is why I didn't want to admit that, like others have said, I find
`categorize` overly complex.

I have an alternate proposition of a modified `categorize` which I
believe addresses the problems I see with it:
1) Complex interface (as was mentioned by others)
2) By default, `categorize` creates a "grouped hash" (like group_by),
while there is not (yet) a way to create a normal hash. I would
estimate that most hash created are not of the form {key => [some
list]} and I would rather have a nicer way to construct the other
hashes too. This would make for a nice replacement for most
"inject({}){...}" and "Hash[enum.map{...}]".


My alternate suggestion is a simple method that uses a block to build
the key-value pairs and an optional Proc/lambda/symbol to handle key
conflicts (with the same arguments as the block of `Hash#merge`). I
would name this simply `associate`, but other names could do well too
(e.g. `mash` or `graph` or even `to_h`).


# enum.associate(merge = nil){ block } # => a_hash
#
# Builds +a_hash+ by iterating on +enum+. If the result of the block
is an array,
# it is interpreted as [key, value]. Otherwise the value is the result
of the block
# and corresponding key is the (first) yielded item.
# In case of key duplication, `merge` will be used. If +nil+, the
value is overwritten;
# if it is a symbol, then it is sent to the first value with the other
value as argument
# otherwise `merge` is called with the arguments
# +key+, +first_value+, +other_value+ (see Hash#merge)
# and the value will be the result of the call.
# Passing no block is equivalent to the block {|v| v}
#
#    (1..4).associate{|i| i ** i} # => {1 => 1, 2 => 2, 3 => 27, 4 => 256}
#    (1..4).associate{|i| ["HELLO"[i], i]} # => {"E" => 1, "L" => 3, "O" => 4}
#    [[:foo, 10], [:bar, 30], [:foo, 32]].associate(:+) # => {:foo =>
42, :bar => 30}
#    {1 => :foo, 2 => "bar"}.associate{|k, v| v.upcase} # => {1 =>
:FOO, 2 => "BAR"}
#    {1 => 2, 3 => 4}.associate{|k, v| [k.to_s, v]} # => {"1" => 2, "3" => 4}

  # A Ruby implementation could look like:

  module Enumerable
    def associate(merge_func = nil)
      if merge_func.is_a? Symbol
        sym = merge_func
        merge_func = ->(k, v1, v2){v1.send(sym, v2)}
      end
      h = {}
      each do |key|
        value = block_given? ? yield(key) : key
        if value.is_a? Array
          key, value = value
        elsif key.is_a? Array
          key = key.first
        end
        if h.has_key?(key) && merge_func
          value = merge_func.call(key, h[key], value)
        end
        h[key] = value
      end
      h
    end
  end


It could of course be argued that both `associate` and `categorize`
should be added. That may very be; I just feel that `associate` should
be added in priority over `categorize`.

I find it very interesting that you posted some questions from
ruby-talk. I've recopied that list with the additional "solutions"
using associate. Some are longer, some are shorter, but I usually find
the associate solution more intuitive (except in the first example).

Thanks
--
Marc-Andr


    * [ruby-talk:351947]
    orig =
      {:day1 => [[:name_person1, :room1], [:name_person2, :room2],
      [:name_person3, :room2]],
        :day2 => [[:name_person1, :room1], [:name_person3, :room1],
      [:name_person2, :room2]]}
    # to
    dest =
      {:room1 => [{:day1 => [:name_person1]},
                  {:day2 => [:name_person1, :name_person3]}],
       :room2 => [{:day1 => [:name_person2, :name_person3]},
                  {:day2 => [:name_person2]}]}
    # This can be implemented as:
    a = orig.map {|k, aa| aa.map {|e| [k, *e] }}.flatten(1)
    dest == a.categorize {|e| [e[2], e[0], e[1]] }
    # For this first example, categorize is pretty natural.
    # The funny thing is that the actual final output desired doesn't
require a hash;
    # the poster is actually complicating his life. Anyways:
    a = orig.flat_map{|day, list| list.associate(:+){|n, r| [r,
[n]]}.map{|r, l| [day, r, l]}}
    dest = int.associate(:+){|day, room, list| [room, [{day => list}]]}


    # * [ruby-talk:347364]

    orig =
     [["2efa4ba470", "00000005"],
      ["2efa4ba470", "00000004"],
      ["02adecfd5c", "00000002"],
      ["c0784b5de101", "00000006"],
      ["68c4bf10539", "00000003"],
      ["c0784b5de101", "00000001"]]
    dest =
     {"2efa4ba470" => ["00000005", "00000004"],
      "02adecfd5c" => ["00000002"],
      "c0784b5de101" => ["00000006", "00000001"],
      "68c4bf10539" => ["00000003"]}

    p dest == orig.categorize {|e| e }
    p dest == orig.associate(:+){|h, v| [h, [v]]}

    # * [ruby-talk:372481]

    orig =
     [["A", "a", 1], ["A", "b", 2], ["B", "a", 1]]
    dest =
     {"A" => {"a" => 1, "b" => 2},
      "B" => {"a" => 1}}

    dest == orig.categorize(:op=>lambda {|x,y| y }) {|e| e }
    dest == orig.associate(:merge){|a, b, c| [a, {b=>c}]}

    # * [ruby-talk:288931]
    # Cut...  is the same case as above

    # * [ruby-talk:354519]

    orig =
     [["200912-829", 9],
      ["200912-893", 3],
      ["200912-893", 5],
      ["200912-829", 1],
      ["200911-818", 6],
      ["200911-893", 1],
      ["200911-827", 2]]
    # to
    dest =
     [["200912-829", 10],
      ["200912-893", 8],
      ["200911-818", 6],
      ["200911-893", 1],
      ["200911-827", 2]]

     This can be implemented as:
    dest == orig.categorize(:op=>:+) {|e| e }.to_a
    dest == orig.associate(:+).to_a

    * [ruby-talk:344723]

    a=[1,2,5,13]
    b=[1,1,2,2,2,5,13,13,13]
    # to
    dest =
     [[0, 0], [0, 1], [1, 2], [1, 3], [1, 4], [2, 5], [3, 6], [3, 7], [3, 8]]

    # This can be implemented as:
     h = a.categorize.with_index {|e, i| [e,i] }
     b.map.with_index {|e, j| h[e] ? h[e].map {|i| [i,j] } : [] }.flatten(1)
    # or
     h = a.each_with_index.associate
     b.map.with_index{|e, i| [h[e], i] }

    # * [ruby-talk:327908]
    orig =
     [["377", "838"],
      ["377", "990"],
      ["377", "991"],
      ["377", "992"],
      ["378", "840"],
      ["378", "841"],
      ["378", "842"],
      ["378", "843"],
      ["378", "844"]]
    # to
    dest =
     [["377", "838 990 991 992"],
      ["378", "840 841 842 843 844"]]

    # This can be implemented as:
     orig.categorize(:seed=>nil, :op=>lambda {|x,y| !x ? y.dup : (x << "
    " << y) }) {|e| e }
    # or
    orig.associate(->(k, a, b){"#{a} #{b}"})
    # or if duping the string is required (??):
    orig.associate(->(k, a, b){a << " " << b}){|x, y| [x, y.dup]}

    # * [ruby-talk:347700]

    orig = ["a", "b", "a", "b", "b"]
    # to
    dest = [["a", "b"], [2, 3]]

    # This can be implemented as:
    h = orig.categorize(:op=>:+) {|e| [e, 1] }
    dest == [h.keys, h.values]
    # or
    h = orig.associate(:+){|e| 1}
    dest == [h.keys, h.values]

    # * [ruby-talk:343511]

    orig =
     [1, 2, 3, 3, 3, 3, 4, 4, 5]
    # to
    dest =
     {3=>4, 4=>2}

    # This can be implemented as:
    h = orig.categorize(:op=>:+) {|e| [e, 1] }
    dest == h.reject {|k,v| v == 1 }
    # or
    h = orig.associate(:+){1}
    dest == h.reject {|k,v| v == 1 }

In This Thread