TL;DR: 如果你總是遇到不知道該用 count, length 或 size,那你可以照下面簡單的步驟依序選擇哪個最適合你目前的應用場景
- 你是否只需要獲得當前物件的數量,如果是,那你應該使用
size
- 除了數量外,你是否還需要存取當前物件的內容,如果是,那你應該使用
length
- 除了以上,你還需要根據條件來計算物件的數量,那你應該使用
count
Note: 以上建議由 1 至 3 做選擇,在多數應用上,效能會是 size >= length >= count
由於這三個方法在不同的物件實作上會有所不同,因此我們將分成 Array 和 CollectionAssociation/ActiveRecord 來說明這三個方法究竟有哪些差別
Array
- size: 等同呼叫 Array#length (Alias)
- length: 回傳物件數量,例如:
[ 1, 2, 3, 4, 5 ].length #=> 5
[].length #=> 0
- count: 此方法可以接受一個參數,會自動比對物件中和參數相等的物件並回傳數量,若參數為 block 則會回傳值為
true
的數量,例如:
[ 1, 2, 3, 4, 5, 1 ].count #=> 6
[ 1, 2, 3, 4, 5, 1 ].count(1) #=> 2
[ 1, 2, 3, 4, 5, 1 ].count { |x| x >= 3 } #=> 3
CollectionAssociation/ActiveRecord
-
size: 執行 SELECT COUNT(*) 回傳數量,例如:
Post.all.size # SELECT COUNT(*) FROM posts
🙅♂️ 若你需要存取物件內容,反而會再次執行一次 SELECT 的 query,此時你應該直接使用
length
,例如:all_posts = Post.all; all_posts.size; all_posts.first; # SELECT COUNT(*) FROM posts # SELECT `posts`.* FROM `posts` ORDER BY `posts`.`id` ASC LIMIT 1
-
length: 會執行 SELECT 取得物件內容,再回傳數量,因此效能會比
size
差,例如:Post.all.length # SELECT `posts`.* FROM `posts`
🙋♂️ 如果在資料 load 的前提上,使用
size
和length
其實是一樣的,例如:all_posts = Post.all.load; all_posts.size; all_posts.length; # SELECT `posts`.* FROM `posts`
-
count: 若不傳任何參數其實和
size
相同,通常在需要計算條件下才會使用,或是搭配 distinct / group 使用,例如:Post.all.count # SELECT COUNT(*) FROM `posts` Post.all.count(:title) # SELECT COUNT(`posts`.`title`) FROM `posts` Post.distinct.count(:title) # SELECT COUNT(DISTINCT `posts`.`title`) FROM `posts` Post.group(:title).count # SELECT COUNT(*) AS `count_all`, `posts`.`title` AS `posts_title` FROM `posts` GROUP BY `posts`.`title`
🙅♂️ 要注意的是,如果參數傳入 block 時,會將資料 load 到記憶體後再進行計算,而不是透過 SQL query 計算,因此可能會有效能問題,例如:
Post.all.count { |post| post.title == 'Hello' } # SELECT `posts`.* FROM `posts`
最後,附上簡單的一張流程圖,希望可以幫助你更快速的選擇該使用哪個方法