When to Use the Length, Size, and Count Methods in Rails

Length, Size, and Count in Ruby and Rails: When to Use Which?

You can use the length, size, and count methods to find the number of elements in a collection. This post explores the difference between these methods and explains how you should choose which method to use according to the needs of your application.

5 min read
There should be one– and preferably only one --obvious way to do it.

- The Zen of Python

Just like Ruby, Rails offers multiple ways to do the same thing. Unlike The Zen of Python, there's no one obvious way to do it. A good example is finding the number of elements in a collection. You can use the length, size, and count methods to accomplish this.

However, it can be confusing to understand when to use which method, as there are a few subtle differences between them, and your choice may have performance implications. Before understanding the difference between these methods, I was never really sure if I had used the correct one.

This post explores the difference between these methods and explains how you should choose which method to use according to the needs of your application.

TL;DR Here's a handy flowchart to simplify your decision-making. It may look daunting, but it really isn't.

When to Use the Length, Size, and Count Methods in Rails

Let's start with a brief overview of these methods in plain Ruby, specifically in the context of an Array. Then we'll move on to Rails.

length

This is the simplest of them all. It's also the method you're most familiar with.

The length method returns the number of elements in an array. It runs in O(1) constant time.

numbers = [1, 2, 3, 4, 5]

numbers.length # 5

It can't get any simpler than this.

size

This is an alias to the length method. If you try to read the Ruby docs for size, it simply redirects you to the length method.

numbers = [1, 2, 3, 4, 5]

numbers.length # 5

Should you use length or size? It's a matter of personal preference, depending on the context in which you need the number of elements in the collection. Choose the one that is most readable for your context.

For example, if I have a collection named classroom that contains a bunch of students, I'd use classroom.size instead of classroom.length. On the other hand, if I am dealing with an array of numbers, I might use length. Both these methods accomplish the same thing, but the flow and focus is subtly different.

count

This method counts the number of specified elements in the array. Use it when you need to find the count of elements matching specific criteria. There are four variations of this method.

1. With no argument and no block, returns the count of all elements

[10, 20, 30].count # => 3
[].count # => 0

2. When an argument is provided, returns the count of array elements that are equal to the argument.

[0, 1, 2, 0.0].count(0) # => 2
[0, 1, 2].count(3) # => 0

3. When a block is provided without any argument, calls the block with each array element and returns the count of elements for which the block returns a truthy value.

[0, 1, 2, 3].count { |e| e > 1 } # => 2

4. If you pass both an argument and a block, it ignores the block and returns the count of elements that are equal to the argument. It also issues a warning.

[1, 2, 3].count(2) { |e| e == 0 }
(irb):2: warning: given block not used
=> 1

That's it for Ruby. Now let's turn our attention to Rails, where things get quite interesting.

Ruby on Rails

We will examine these methods in the context of the ActiveRecord::Associations::CollectionProxy class, which inherits from the ActiveRecord::Relation class.

💡
Collection proxies are used by ActiveRecord, acting as middlemen between an association and its result set.
class Post < ActiveRecord::Base
  has_many :comments
end

post = Post.last
comments = post.comments

# Comment::ActiveRecord_Associations_CollectionProxy
comments.class 

In this example, comments is a collection proxy, delegating to a collection of posts. Now let's understand how these methods work on a collection proxy.

size

If the collection hasn’t been loaded, the size method executes a SELECT COUNT(*) query. Otherwise, it calls collection.size.

Post.all.size
# SELECT COUNT(*) FROM `posts`

Behind the scenes, this is how the size method works.

size vs. length in Rails
size vs. length in Rails

length

Returns the size of the collection by calling the size method on it.

Post.all.length
# SELECT `posts`.* FROM `posts`

That means, if the collection has been already loaded, length and size are equivalent.

When to use length vs. size ?

When the collection is already loaded, the size and length methods are equal.

If the collection is not loaded from the database:

  1. If you will need the records anyway, use the length method. It will take one less query, since Rails will use the cached records for the later query.
  2. If you won't need the records, use the size method as it's more efficient. It will run a COUNT SQL query to fetch the count directly from the database, without loading the records in memory.
post.comments.length
  Post Load (2.4ms)  SELECT `posts`.* FROM `posts` ORDER BY `posts`.`id` ASC LIMIT 1
  Comment Load (0.5ms)  SELECT `comments`.* FROM `comments` WHERE `comments`.`post_id` = 1

post.comments.size
  Post Load (0.4ms)  SELECT `posts`.* FROM `posts` ORDER BY `posts`.`id` ASC LIMIT 1
  Comment Count (1.9ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 1

count

Just like Ruby's count method, the count method in Rails counts all the records in a collection that satisfy a given condition. There are two variations of this method:

1. When a block is not provided, returns the count of all records in the collection

posts = Post.all

# SELECT COUNT(*) FROM `posts`
posts.count

2. When a block is provided, calls the block with each collection element and returns the count of elements for which the block returns a truthy value.

Post.all.count { |p| p.title.include?('-') }

I use the count method when I need the count of elements that match a certain criteria. Otherwise, I just stick to one of the length or size methods.


That's a wrap. I hope you found this article helpful and you learned something new.

As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I reply to all emails I get from developers, and I look forward to hearing from you.

If you'd like to receive future articles directly in your email, please subscribe to my blog. If you're already a subscriber, thank you.