DEV Community

loading...
Cover image for Understanding Ruby - Enumerable - Counting

Understanding Ruby - Enumerable - Counting

Brandon Weaver
Staff Eng / Ruby Lead / Global Neurodiversity Chair at @Square. Autistic / ADHD, He / Him. I'm the Lemur guy.
・3 min read

Introduction

Enumerable. Debatably one of, if not the, most powerful features in Ruby. As a majority of your time in programming is dealing with collections of items it's no surprise how frequently you'll see it used.

Difficulty

Foundational

Some knowledge required of functions in Ruby. This post focuses on foundational and fundamental knowledge for Ruby programmers.

Prerequisite Reading:

Enumerable

Enumerable is an interface module that contains several methods for working with collections. Many Ruby classes implement the Enumerable interface that look like collections. Chances are if it has an each method it supports Enumerable, and because of that it's quite ubiquitous in Ruby.

Note: This idea was partially inspired by Lamar Burdette's recent work on Ruby documentation, but takes its own direction.

Counting

It's the thought that counts, or is it your code? Well Ruby gives various methods for counting, tallying, or otherwise summing up your collections.

#count

count counts the number of elements in a collection that match a condition. When supplied no arguments it returns how many elements are in the collection:

[1, 2, 3, 4, 5].count
# => 5
Enter fullscreen mode Exit fullscreen mode

When given a number it searches for that number and gives a count of how many occurrences it found:

[1, 1, 2, 2, 3, 3, 3].count(3)
# => 3
Enter fullscreen mode Exit fullscreen mode

...and finally when given a Block Function it returns back how many elements matched a condition inside of it:

[1, 1, 2, 2, 3, 3, 3].count(&:odd?)
# => 5
Enter fullscreen mode Exit fullscreen mode

count is one of those methods which comes in handy all the time, and I frequently use it along with the other two methods in this section.

#sum

sum gets the sum of the elements in a collection:

[1, 2, 3].sum
# => 6
Enter fullscreen mode Exit fullscreen mode

It also takes an initial value to start summing from:

[1, 2, 3].sum(1)
# => 7
Enter fullscreen mode Exit fullscreen mode

It can also take a Block Function to define how to sum each element:

[1, 2, 3].sum { |v| v * 2 }
# => 12
Enter fullscreen mode Exit fullscreen mode

Do note that's not a product, we'd need reduce in a moment for that.

#tally

tally is admittedly a point of pride for me as me and a group of friends at RailsCamp had a hand in naming it.

It used to be that you had to do this to get counts indexed by a key:

%w(a fresh lively lemur jumps over a tea kettle)
  .group_by(&:itself)
  .map { |k, vs| [k, vs.size] }
  .to_h
# => {"a"=>2, "fresh"=>1, "lively"=>1, "lemur"=>1, "jumps"=>1, "over"=>1, "tea"=>1, "kettle"=>1}

# or
%w(a fresh lively lemur jumps over a tea kettle)
  .each_with_object(Hash.new(0)) { |v, h| h[v] += 1 }
# => {"a"=>2, "fresh"=>1, "lively"=>1, "lemur"=>1, "jumps"=>1, "over"=>1, "tea"=>1, "kettle"=>1}
Enter fullscreen mode Exit fullscreen mode

Now with tally you can do this:

%w(a fresh lively lemur jumps over a tea kettle).tally
# => {"a"=>2, "fresh"=>1, "lively"=>1, "lemur"=>1, "jumps"=>1, "over"=>1, "tea"=>1, "kettle"=>1}
Enter fullscreen mode Exit fullscreen mode

That's much more pleasant. Consider combining it with map to do even more interesting things:

%w(a fresh lively lemur jumps over a tea kettle)
  .map(&:size)
  .tally
# => {1=>2, 5=>3, 6=>2, 4=>1, 3=>1}
Enter fullscreen mode Exit fullscreen mode

tally is one of my most used functions next to map, filter, and reduce. It's really handy to get a quick look at an overview of the data you're looking at in a concise way.

Wrapping Up

The next few articles will be getting into the various parts of Enumerable, grouped by functionality:

  1. Transforming
  2. Predicate Conditions
  3. Searching and Filtering
  4. Sorting and Comparing
  5. Counting
  6. Grouping
  7. Combining
  8. Iterating and Taking
  9. Coercion

While lazy is part of Enumerable that deserves a post all its own, and we'll be getting to that one soon too.

Want to keep up to date on what I'm writing and working on? Take a look at my new newsletter: The Lapidary Lemur

Discussion (0)