In plain Ruby, there’s effectively no difference between count, size, and length when working with arrays or other collection-like objects — they all return the number of elements.
arr = [1, 2, 3]
arr.count # => 3
arr.size # => 3
arr.length # => 3
However, in Ruby on Rails, particularly when dealing with ActiveRecord associations or query objects, these methods behave very differently under the hood — especially in terms of performance and when they trigger database queries.
Understanding these differences is crucial when you're working with large datasets or optimizing performance in a Rails application.
Let's take an example:
users = User.where(active: true)
users.class # User::ActiveRecord_Relation # its returns ActiveRecord, does not execute the query in this stage
At this point, users is an ActiveRecord::Relation, not an array. That means the database query has not been executed yet — it's just a representation of the query to be run later.
This is a key feature of ActiveRecord: lazy loading. The query will only be executed when the data is actually needed — for example, when you iterate over the records, call .to_a, or use .length.
.count
Since users is an ActiveRecord::Relation, calling .count on it will immediately execute a SQL COUNT(*) query to count the number of matching records — without loading the actual records into memory.
[51] pry(main)> users.count
(0.6ms) SELECT COUNT(*) FROM "users" WHERE "users"."active" = $1 /*application:Pixie*/ [["active", true]]
=> 32
✅ This is efficient when you only need the number of records, not the records themselves.
⚠️ Note: .count always queries the database, even if the relation was previously loaded.
.length
The .length method always loads the records into memory and then calculates the number of elements by calling Array#length.
users = User.where(active: true)
users.length # => runs SELECT * FROM users WHERE active = true
This converts the relation into an array and then returns the number of elements.
⚠️ Important: This can be inefficient for large datasets because it loads all matching records into memory, even if you only need the count.
✅ Use .length only when you actually need the full records — for example, if you're going to iterate over them right after.
.size
The .size method is smart — it adapts based on whether the records have already been loaded into memory.
✅ If the records are already loaded, .size simply returns the number of records in memory:
users.loaded?
# => true
users.size
# => 32 (No additional SQL query is executed)
✅ If the records are not yet loaded, .size behaves like .count and runs a SQL COUNT(*) query:
users.loaded?
# => false
users.size
# => Executes:
# SELECT COUNT(*) FROM "users" WHERE "users"."active" = $1
# => 32
This makes .size the most efficient and flexible choice in many cases — it avoids unnecessary queries but still gives an accurate count.
Top comments (1)
+useful