Read more

Custom Ruby method Enumerable#count_by (use for quick statistics)

Dominik Schöler
August 22, 2017Software engineer at makandra GmbH

I frequently find myself needing a combination of group_by, count and sort for quick statistics. Here's a method on Enumerable that combines the three:

module Enumerable
  def count_by(&block)
    list = group_by(&block)
      .map { |key, items| [key, items.count] }
      .sort_by(&:last)
      
    Hash[list]
  end
end

# Returns a Hash of { key => count } pairs (see below)
Illustration online protection

Rails professionals since 2007

Our laser focus on a single technology has made us a leader in this space. Need help?

  • We build a solid first version of your product
  • We train your development team
  • We rescue your project in trouble
Read more Show archive.org snapshot

Just paste that snippet into a Rails console and use #count_by now!

Usage examples

  • Number of email addresses by domain:
> User.all.count_by { |user| user.email.sub /^.*@/, '' }
=> { "sina.cn"=>2, ..., "hotmail.com"=>128, "gmail.com"=>153}
  • Number of new users per day: User.all.count_by { |user| user.created_at.to_date }
  • Number of articles per brand: Article.all.count_by &:brand

Note that the last simple example can also be achieved with Rails internals: Article.group(:brand).count. This translates to SQL, so it executes fast. However, grouping is restricted to columns (attributes). Using #count_by gives you the full flexibility of Ruby.

More tools

If you need further options, here's a "toolbox" of chainable method invocations:

list
  .group_by { |item| item.id }                                # Group
  .map { |key, items| [key, items.count] }                    # Count
  .select { |key, count| count > 10 }                         # Filter
  .sort_by(&:last)                                            # Sort ASC
  .reverse                                                    # Sort DESC
  .each { |key, count| puts "#{count.to_s.rjust(6)} #{key}" } # Print

Wrap the result in Hash[...] to turn a list-of-pairs into a Hash.

Posted by Dominik Schöler to makandra dev (2017-08-22 14:08)