From: Ralph Shnelvar Date: 2012-01-17T10:39:40+09:00 Subject: Re: uniq with count; better way? Abinoam, Monday, January 16, 2012, 6:05:33 PM, you wrote: AJ> 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. AJ> Some benchmark results... AJ> n = 100_000 AJ> Benchmark.bm(15) do |b| AJ> b.report("Ralph Shneiver:") { n.times { a = [4,5,6,4,5,6,6,7]; AJ> result = Hash.new(0); a.each { |x| result[x] += 1 }; result} } AJ> b.report("Sigurd:") { n.times { AJ> [4,5,6,4,5,6,6,7].inject(Hash.new(0)) {|res, x| res[x] += 1; res } } } AJ> b.report("Keinich #1") { n.times { Hash[a.group_by{|n|n}.map{|k, AJ> v|[k, v.size]}] } } AJ> b.report("Keinich #2") { n.times { AJ> Hash.new(0).tap{|h|a.each{|n|h[n] += 1}} } } AJ> b.report("Magnus Holm:") { n.times { AJ> [4,5,6,4,5,6,6,7].each_with_object(Hash.new(0)) { |x, res| res[x] += 1 AJ> } } } AJ> b.report("Abinoam #1:") { n.times { Hash[ AJ> [4,5,6,4,5,6,6,7].sort.chunk {|n| n}.map {|ix, els| [ix, els.size] } ] AJ> } } AJ> end AJ> user system total real AJ> Ralph Shneiver: 0.290000 0.000000 0.290000 ( 0.259640) AJ> Sigurd: 0.320000 0.000000 0.320000 ( 0.289873) AJ> Keinich #1 0.560000 0.000000 0.560000 ( 0.497736) AJ> Keinich #2 0.280000 0.000000 0.280000 ( 0.250843) AJ> Magnus Holm: 0.310000 0.000000 0.310000 ( 0.283344) AJ> Abinoam #1: 1.140000 0.000000 1.140000 ( 1.042744) AJ> Abinoam Jr. Wow ... thank you!