081c31620e9ed8dca87afeeeeb279f68

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.

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 !

D41d8cd98f00b204e9800998ecf8427e

bob

June 26, 2010, June 26, 2010 23:53, permalink

1 rating. Login to rate!

simply wrap group_by

class Array
  def count_by(&block)
    Hash[group_by(&block).collect{|x,g| [x,g.size]}]
  end
end
081c31620e9ed8dca87afeeeeb279f68

matt

June 27, 2010, June 27, 2010 23:33, permalink

No rating. Login to rate!

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
A3897deec9b1dbc6def9f5dfcb58bb60

spaghetticode

June 28, 2010, June 28, 2010 17:55, permalink

2 ratings. Login to rate!
class Array
  def count_by
    inject(Hash.new{|k,v| k=0}) do |res, e|
      res[yield(e)] += 1
      res
    end
  end
end
7855792dbc5f3b4c365344314e2b1ad6

arvanasse

June 30, 2010, June 30, 2010 13:55, permalink

1 rating. Login to rate!

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
Aaf063059bc7763568ba3c44887135e2

joe

December 6, 2010, December 06, 2010 06:21, permalink

No rating. Login to rate!

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

Your refactoring





Format Copy from initial code

or Cancel