From: Tanaka Akira Date: 2010-12-25T17:45:07+09:00 Subject: [ruby-core:33869] Re: [feature:trunk] Enumerable#categorize 2010/12/20 "Martin J. D�rst" : >> Enumerable#categorize is more general than Enumerable#group_by. > > I know. I think it's way too general and difficult to understand. I don't think it is too difficult. When we create hash, it is natural that we can specify keys and values. But we can specify only keys for Enumerable#group_by. I feel it is restricted. > This is one example. I don't deny that such examples exist. But I think they > are not so frequent, and quite varied (i.e. there are many examples where > one needs "almost something like this, but not quite exactly the same). > > I think that unless we can boil down your proposal to something simple that > the average advanced Ruby programmer can understand and use without having > to look it up or try it out in irb all the time, we have to invest some more > time to find the right method. > > After all, group_by was adapted by Ruby 1.9 after a lot of field experience > in Rails. And I have personally used it many times, as I think others have, > too. > > On the other hand, do you have that much field experience? How many others > have told you that they would have used categorize a few times already if it > existed? (As opposed to those who have said that it's too complicated to > remember what it does, and too easy to write the equivalent by hand if > needed.) I transformed CSV tables variously last several monthes in my work. Enumerable#categorize is very useful for that. (Unfortunately, I cannot tell you the exact examples though.) For split a CSV table: enum.categorize {|rec| [split-key, ...] }. For merge CSV tables: Enumerable#categorize can be used for hash-join algorism as enum.categorize {|rec| [join-key, ...] } http://en.wikipedia.org/wiki/Hash_join For counting number for each category: enum.categorize(:op=>:+) {|e| [key, 1] } Also various people asks about similar hash creation. * [ruby-talk:351947] {day1 => [[name_person1, room1], [name_person2, room2], [name_person3, room2]], day2 => [[name_person1, room1], [name_person3, room1], [name_person2, room2]]} to [{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) pp a.categorize {|e| [e[2], e[0], e[1]] } * [ruby-talk:347364] [["2efa4ba470", "00000005"], ["2efa4ba470", "00000004"], ["02adecfd5c", "00000002"], ["c0784b5de101", "00000006"], ["68c4bf10539", "00000003"], ["c0784b5de101", "00000001"]] to {"2efa4ba470" => ["00000005", "00000004"], "02adecfd5c" => ["00000002"], "c0784b5de101" => ["00000006", "00000001"], "68c4bf10539" => ["00000003"]} This can be implemented as: orig.categorize {|e| e } * [ruby-talk:372481] [["A", "a", 1], ["A", "b", 2], ["B", "a", 1]] to {{"A" => {"a" => 1, "b" => 2}}, {"B" => {"a" => 1}}} This can be implemented as: orig.categorize(:op=>lambda {|x,y| y }) {|e| e } * [ruby-talk:288931] [["1", "01-02-2008", 5], ["1", "01-03-2008", 10], ["2", "12-25-2007", 5], ["1", "01-04-2008", 15]] to {"1" => {"01-02-2008" => 5, "01-03-2008" => 10, "01-04-2008" => 15}, "2" => {"12-25-2007" => 5}} This can be implemented as: orig.categorize(:op=>lambda {|x,y| y}) {|e| e } * [ruby-talk:354519] [["200912-829", 9], ["200912-893", 3], ["200912-893", 5], ["200912-829", 1], ["200911-818", 6], ["200911-893", 1], ["200911-827", 2]] to [["200912-829", 10], ["200912-893", 8], ["200911-818", 6], ["200911-893", 1], ["200911-827", 2]] This can be implemented as: orig.categorize(:op=>:+) {|e| e }.to_a * [ruby-talk:344723] a=[1,2,5,13] b=[1,1,2,2,2,5,13,13,13] to [[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) * [ruby-talk:327908] [["377", "838"], ["377", "990"], ["377", "991"], ["377", "992"], ["378", "840"], ["378", "841"], ["378", "842"], ["378", "843"], ["378", "844"]] to [["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 } * [ruby-talk:347700] ["a", "b", "a", "b", "b"] to ["a", "b"] [2, 3] This can be implemented as: h = orig.categorize(:op=>:+) {|e| [e, 1] } p h.keys, h.values * [ruby-talk:343511] [1, 2, 3, 3, 3, 3, 4, 4, 5] to {"3"=>4, "4"=>2} This can be implemented as: h = orig.categorize(:op=>:+) {|e| [e, 1] } p h.reject {|k,v| v == 1 } I feel many people needs hash creation. Enumerable#categorize support them. > An additional thought: The example above starts with two-element arrays. > Such two- or multi-element arrays are often used, but in many cases they are > just an intermediate step, before creating objects. group_by seems more > close to using objects (that may be why it is used a lot in Rails, where the > basics of model classes are almost free). On the other hand, with > multi-element arrays, I think that part of what "categorize" would do will > often be handled before or after. Anyway, while we should not change Ruby so > that it is too difficult to use multi-element arrays instead of objects, > there is also no reason to create more methods that work better for > multi-element arrays. Ruby doesn't force us to create a class for programming. I think this is a good aspect for scripting area. > That may be true. But even if the average number of options for a Ruby > method has slightly increased recently, your proposals still is way over > average on the number of options, especially in an area (iterators on > Enumerable) where options are few and far between. It is natural because Enumerable is exist from old time. I don't see any problem. -- Tanaka Akira