class Array
def count_by(&block)
res={}
each do |e|
res[key=yield(e)] ||= 0
res[key]+=1
end
res
end
end
# Example
[1,2,3,3,3,4,4,5,6,6,2].count_by do |i|
if (i % 2 == 0)
:even
else
:odd
end
end
# => {:even => 6, :odd => 5}
Refactorings
No refactoring yet !
bob
June 26, 2010, June 26, 2010 23:53, permalink
simply wrap group_by
class Array
def count_by(&block)
Hash[group_by(&block).collect{|x,g| [x,g.size]}]
end
end
matt
June 27, 2010, June 27, 2010 23:33, permalink
Very nice. One drawback... It appears to work in >1.8.7 but not 1.8.6 (which, I unfortunately still need to use). Here's a solution using inject that appears to work in 1.8.6 and 1.8.7.
class Array
def count_by(&block)
group_by(&block).inject({}) do |h,(k,v)|
h[k] = v.size
h
end
end
end
spaghetticode
June 28, 2010, June 28, 2010 17:55, permalink
class Array
def count_by
inject(Hash.new{|k,v| k=0}) do |res, e|
res[yield(e)] += 1
res
end
end
end
arvanasse
June 30, 2010, June 30, 2010 13:55, permalink
Pretty similar to spaghetticode but a little cleaner/clearer (IMO) because it makes the key calculation more obvious and does not have to explicitly return the hash. To each his own, I guess. :)
class Array
def count_by
inject( Hash.new(0) ) do |result, val|
key = yield val
result.merge key => result[key] + 1
end
end
end
joe
December 6, 2010, December 06, 2010 06:21, permalink
This should work in 1.8.6
class Array
def count_by(&block)
Hash[*group_by(&block).collect{|x,g| [x,g.size]}.flatten]
end
end
I want an array method that works like group_by but the values are the count of elements that match the key. I know there is a cleaner solution.