From: Marc-Andre Lafortune Date: 2010-12-29T17:58:49+09:00 Subject: [ruby-core:33991] Re: [feature:trunk] Enumerable#categorize Hi! On Tue, Dec 28, 2010 at 1:45 PM, Tanaka Akira wrote: I agree with most of what you wrote. Here are comments on some parts: > I'm not sure that matz will satisfy the name 'associate'. Or `mash`, `make_hash` or `build_hash`. > * 'associate' doesn't create nested hash. > > �'associate' is simpler here. > > �I think 'associate' can be extended naturally that the method creates > �nested hash when the block returns an array with 3 or more elements. Indeed, that could be. I need to read again your implementation for details. > * 'associate' assumes {|v| v } if the block is not given. > > �This simplify some usages. > �However this forbids Ruby 1.9 style enumerator creation > �which returns an enumerator when block is not given. > �This means we cannot write enum.associate.with_index {|v, i| ... }. Indeed, but you can write `enum.each_with_index.associate.{|v, i| ... }` gives the same result, and `to_enum` is always available. The no block form being {|v| v} can be changed, of course. I originally wanted to propose this as `to_h`, and it thus had to be idempotent on any Hash. If it is not called `to_h`, then the form without a block can return an enumerator. I find that returning an enumerator is super useful on iterators that modify how the iteration is done (each_with_index, each_slice, ...), but not so useful on iterators that create objects (map, all?, ...); I tend to stick these at the end of the chain. Either way can work; it doesn't change the important part of the proposal. > * 'associate' treates non-array block value. > > �This is more complex than 'categorize'. > > �I feel it is bit distorted specification. > �Especially "(first)" in "Otherwise the value is the result of the block > �and corresponding key is the (first) yielded item." For the return value, we should look into the possibility merging categorize & associate. For the "default index" when the return value doesn't provide one, I agree it's not "pure". Again, this was because I wanted it to be idem potent on hashes, but I feel it is much more useful anyways. I don't recall ever creating a hash with arrays as keys. I feel that Ruby favors the practical side of things over purity. > �However symbol and symbol.to_proc will be different, though. Yes, but I feel that's normal, like the fact that `inject(:+)` and `inject(&:+)` are different. > * 'associate' doesn't have a way to specify the seed. > > �This is simpler specification than 'categorize' > �but this makes some usages more complex. I'm curious as to what usage you are thinking. I feel that at the block level, you should assume that this is first time you associate a key with a value, as conflicts are the responsibility of the "merge" argument. If a seed is needed, it should go in the block. Also, I feel that seeds can be easily misunderstood(e.g. are they dupped like slice_before/chunk? just copied by reference like Hash.new(seed)?) This adds a level of complexity which can be avoided here. > �In your 'associate' examples for [ruby-talk:347364] and > �[ruby-talk:327908], array and string concatenation is O(n**2). Indeed. To make it O(n), replace `:+` by `:concat`. > Actually I want one more method for counting. > (I want 3 methods: grouped hash, normal hash, count hash) A count hash could be useful, indeed. Again, I think `associate` is much more needed. Indeed, `inventory{|x| whatever}` can be computed with `associate(:+){|x| [whatever, 1]}`. >> I just feel that `associate` should >> be added in priority over `categorize`. > > matz felt similar. �[ruby-dev:42643] I'm glad to hear that :-) Has another specification been proposed and how does it compare to `associate`? > Your solution depends on 'a' has no duplicated elements. Oh, right, I didn't read the original ruby-talk post. This doesn't change the comparison of the first line which is what matters. So let my second line use flat_map+fetch :-) h = a.categorize.with_index {|e, i| [e,i] } p dest == b.map.with_index {|e, j| h[e] ? h[e].map {|i| [i,j] } : []}.flatten(1) # compared to h = a.each_with_index.associate p dest == b.flat_map.with_index {|e, j| h.fetch(e){[]}.map {|i| [i,j] }} Thanks -- Marc-Andr�