ruby-core

Mailing list archive

[#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 =3D=3D 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 =3D> [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 =3D nil){ block } # =3D> 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} # =3D> {1 =3D> 1, 2 =3D> 2, 3 =3D> 27, 4 =
=3D> 256}
#    (1..4).associate{|i| ["HELLO"[i], i]} # =3D> {"E" =3D> 1, "L" =3D> 3, =
"O" =3D> 4}
#    [[:foo, 10], [:bar, 30], [:foo, 32]].associate(:+) # =3D> {:foo =3D>
42, :bar =3D> 30}
#    {1 =3D> :foo, 2 =3D> "bar"}.associate{|k, v| v.upcase} # =3D> {1 =3D>
:FOO, 2 =3D> "BAR"}
#    {1 =3D> 2, 3 =3D> 4}.associate{|k, v| [k.to_s, v]} # =3D> {"1" =3D> 2,=
 "3" =3D> 4}

  # A Ruby implementation could look like:

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


    * [ruby-talk:351947]
    orig =3D
      {:day1 =3D> [[:name_person1, :room1], [:name_person2, :room2],
      [:name_person3, :room2]],
        :day2 =3D> [[:name_person1, :room1], [:name_person3, :room1],
      [:name_person2, :room2]]}
    # to
    dest =3D
      {:room1 =3D> [{:day1 =3D> [:name_person1]},
                  {:day2 =3D> [:name_person1, :name_person3]}],
       :room2 =3D> [{:day1 =3D> [:name_person2, :name_person3]},
                  {:day2 =3D> [:name_person2]}]}
    # This can be implemented as:
    a =3D orig.map {|k, aa| aa.map {|e| [k, *e] }}.flatten(1)
    dest =3D=3D 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 =3D orig.flat_map{|day, list| list.associate(:+){|n, r| [r,
[n]]}.map{|r, l| [day, r, l]}}
    dest =3D int.associate(:+){|day, room, list| [room, [{day =3D> list}]]}


    # * [ruby-talk:347364]

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

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

    # * [ruby-talk:372481]

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

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

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

    # * [ruby-talk:354519]

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

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

    * [ruby-talk:344723]

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

    # This can be implemented as:
     h =3D 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 =3D a.each_with_index.associate
     b.map.with_index{|e, i| [h[e], i] }

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

    # This can be implemented as:
     orig.categorize(:seed=3D>nil, :op=3D>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 =3D ["a", "b", "a", "b", "b"]
    # to
    dest =3D [["a", "b"], [2, 3]]

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

    # * [ruby-talk:343511]

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

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

In This Thread