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

Updated . Posted . Visible to the public.

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)
    group_by(&block)
      .transform_values(&:count)
      .sort_by(&:last)
      .to_h
  end
end

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
  .to_h
Profile picture of Dominik Schöler
Dominik Schöler
Last edit
Dominik Schöler
License
Source code in this card is licensed under the MIT License.
Posted by Dominik Schöler to makandra dev (2017-08-22 12:08)