From: "Abinoam Jr." Date: 2012-01-17T10:44:51+09:00 Subject: Re: uniq with count; better way? On Mon, Jan 16, 2012 at 10:05 PM, Abinoam Jr. wrote: > On Mon, Jan 16, 2012 at 9:22 PM, Abinoam Jr. wrote: >> On Mon, Jan 16, 2012 at 1:48 PM, Magnus Holm wrote: >>> On Mon, Jan 16, 2012 at 17:04, Adam Prescott wrote: >>>> On Mon, Jan 16, 2012 at 16:00, Sigurd wrote: >>>> >>>>> [4,5,6,4,5,6,6,7].inject(Hash.new(0)) {|res, x| res[x] += 1; res } >>>>> >>>> >>>> I think this is a misuse of inject, personally, every time I see it. It's >>>> harder to read and it doesn't give the feeling of actually "reducing" >>>> (inject's alias) the array down to one thing. The required `; res` is a >>>> sign of that. Compare: >>>> >>>> [1, 2, 3, 4].inject(5) { |a, b| a + b } >>> >>> There's always each_with_object, although it's a little long: >>> >>>  [4,5,6,4,5,6,6,7].each_with_object(Hash.new(0)) { |x, res| res[x] += 1 } >>> >> >> I think Magnus Holm is the clearest (IMHO, yes, it's just a taste and >> humble opinion.). >> >> [4,5,6,4,5,6,6,7].each_with_object(Hash.new(0)) {|num, hsh| hsh[num] += 1} >> >> Another way (not better) I remember is... >> >> Hash[ [4,5,6,4,5,6,6,7].sort.chunk {|n| n}.map {|ix, els| [ix, els.size] } ] >> >> See: http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-chunk >> >> It also can be... clearer?!? >> >> Hash[ [4,5,6,4,5,6,6,7].group_by {|n| n}.map {|ix, els| [ix, els.size] } ] >> >> Perhaps something like this (same as Magnus Holm) just hiding the >> complexity into the method. >> >> class Array >>  def totalize_to_hash >>    hsh = Hash.new(0) >>    self.each do |n| >>      hsh[n] += 1 >>    end >>    hsh >>  end >> end >> >> [4,5,6,4,5,6,6,7].totalize_to_hash >> >> Abinoam Jr. > > Some benchmark results... > > n = 100_000 > Benchmark.bm(15) do |b| >  b.report("Ralph Shneiver:") { n.times { a = [4,5,6,4,5,6,6,7]; > result = Hash.new(0); a.each { |x|  result[x] += 1 }; result} } >  b.report("Sigurd:")        { n.times { > [4,5,6,4,5,6,6,7].inject(Hash.new(0)) {|res, x| res[x] += 1; res } } } >  b.report("Keinich #1")     { n.times { Hash[a.group_by{|n|n}.map{|k, > v|[k, v.size]}] } } >  b.report("Keinich #2")     { n.times { > Hash.new(0).tap{|h|a.each{|n|h[n] += 1}} } } >  b.report("Magnus Holm:")   { n.times { > [4,5,6,4,5,6,6,7].each_with_object(Hash.new(0)) { |x, res| res[x] += 1 > } } } >  b.report("Abinoam #1:")    { n.times { Hash[ > [4,5,6,4,5,6,6,7].sort.chunk {|n| n}.map {|ix, els| [ix, els.size] } ] > } } > end > >                     user     system      total        real > Ralph Shneiver:   0.290000   0.000000   0.290000 (  0.259640) > Sigurd:          0.320000   0.000000   0.320000 (  0.289873) > Keinich #1       0.560000   0.000000   0.560000 (  0.497736) > Keinich #2       0.280000   0.000000   0.280000 (  0.250843) > Magnus Holm:     0.310000   0.000000   0.310000 (  0.283344) > Abinoam #1:      1.140000   0.000000   1.140000 (  1.042744) > > Abinoam Jr. > Sorry for the mess... (some error in the bench code + horrible text wrapping) Apologizing with the gist... https://gist.github.com/1624016 Abinoam Jr.