Ruby has three different methods for counting the items in an Enumerable: length, count, and size. Coming from a Python background ("There should be one— and preferably only one —obvious way to do it."), the Ruby mentality of "more than one way to do it" regularly catches me off guard. In most cases, I eventually come around and appreciate having multiple options, but with counting, I'm wasn't sure of the benefits. So I did some research.

Three ways to count

The following code snippet demonstrates how an Array can call each of the Enumerable counting methods:

some_array = [1,2,3,4,5,6,7]
=> [1, 2, 3, 4, 5, 6, 7]

some_array.length
=> 7

some_array.count
=> 7

some_array.size
=> 7

Other enumerable classes, including Hash and Range, operate similarly. So when should each method be used?

To length or to size?

It turns out that length is the original item-counting method, while size is just an alias. In general, that means that it's perfectly fine to use the two interchangeably. Except when length is better.

But why have an alias? While the original purpose for aliasing can only be confirmed by the Ruby Core member who first implemented it, this Stack Overflow explanation offered a sufficiently plausible reason for the alias to exist. In short, it suggests that a developer could override the size method if desired without losing access to the original length implementation. Though not a completely satisfactory answer for my own tastes, I do think the explanation has merit.

...or to count?

So what about count? Well, count is not an alias! It actually accepts a Ruby block and counts how often that block evaluates to true for each item in the enumerable:

some_array.count(7)
=> 1

some_array.count(&:even?)
=> 3

When no block is passed, every item automatically evalutes to true, so every item gets counted. But since this means that count will walk through all entries of an enumerable every time, it's unlikely to be as performance-optimized as length and shouldn't be used interchangeably.

Enumerables != Active Records

All the above explanations only apply for Enumerable classes. This can be confusing since Active Records also have length, count, and size methods.

For Active Records, length counts the number of entries that are already loaded into memory and avoids database calls, while count always makes the database call (i.e. SELECT COUNT(*)) to get the most accurate count. And size is an "alias" that knows when to use length or count under the hood so that you don't have to think about it.

It may seem like length and count should always return the same counts, but this isn't the case. Depending on how new records are created, it's possible for unsaved records to exist only in memory but not the database or for relationships between two Active Record models to exist in the database but not yet be established in memory. This blog entry succinctly discusses the effect of lazy relationship loading.

Conclusion

There is a difference between length, count, and size. Maybe Ruby doesn't have multiple ways to count items after all.