The difference between length, size, and count in Ruby on Rails

I was asked recently if I could explain the difference between length, size, and count for ActiveRecord models in Ruby on Rails. Unfortunately I had no answer. But I wanted to really understand so I dug into the API docs.

On a Ruby Array the methods size, count, and length are as such; size is an alias for length, length simply returns a count of the all elements in the array, and count is pretty neat in that you can pass in a block to count elements based on some condition. If you do not pass any arguments to count then it returns a count of all items.

eg: count all even numbers from array

arr = [1, 2, 3, 4, 5, 6, 7]
arr.count { |x| x%2 == 0 } 
=> 3

But on Rails models these operate a bit differently. It's good to know the differences because each have their utility.

I have a test app with a User model with one record. On the model itself only count works.

> User.count
User Count (0.7ms)  SELECT COUNT(*) FROM "users"
=> 1

User.length
>  undefined method `length' for User:Class (NoMethodError)

User.size
> undefined method `size' for User:Class (NoMethodError)

But on a collection:

user = User.all
User.count
> User Count (2.9ms)  SELECT COUNT(*) FROM "users"
=> 1

user.size
=> 1

user.length
=> 1

Here is a call to size on a collection that is not yet loaded. It executes a COUNT(*).

> User.all.size
User Count (4.0ms)  SELECT COUNT(*) FROM "users"
=> 1

count on a collection will always execute a COUNT(*) SQL statement to get an accurate count value. size returns the size of the collection, but will execute a COUNT(*) SQL statement if it's not already loaded. length returns the size of the collection from memory. If the collection has already been loaded and is in memory length and size are equivalent.

Relying on what's in memory may give you inaccurate results as the database could be modified while you are working on that collection. But making a bunch of expensive database calls to get an accurate count is probably something you want to avoid. There may be times when a count is preferred, but when in doubt it's probably better to use length.