很簡單的一個問題,但其實了解後蠻好玩的。
length : 長度。
size : 尺寸。
count : 計數。
size
&length
。
在陣列與Hash這種長條狀的資料上,size
與length
基本上一樣,也可說是size
是length
的別名,陣列與Hash在記憶體中,就是一條長條狀的儲存方式,所以長度本就比尺寸來形容更恰當。length比size要多打2個字母啦。
2.7.3 :009 > [1, 2, 3, 4].size
=> 4
2.7.3 :010 > [1, 2, 3, 4].length
=> 4
或者該說,記憶體型態上如果是這種長條型的,length
與size
沒有差別,都是能瞭解長度
的同名方法而已了。
例如Integer
這個型態就沒有length
,雖然一般size
都是8。
2.7.3 :001 > 8.size
=> 8
2.7.3 :002 > 8.length
Traceback (most recent call last):
(irb):2:in `<main>': undefined method `length' for 8:Integer (NoMethodError)
count
。length
、size
、count
都是計數方法,但count
在執行上不是只有計算長度或尺寸,本身也是迭代枚舉產生器,可以對每一個元素作紀錄或操作,沒有給予條件的話回傳有幾個元素。所以在Ruby
單純算長度等不要用count
,會稍微慢一點點。
2.7.3 :018 > [1, 2, 3, 4].count
=> 4
2.7.3 :019 > [1, 2, 3, 4].count(3)
=> 1
2.7.3 :020 > [1, 2, 3, 4].count {|num| num > 2}
=> 2
某些Class就不能直接以count
計算長度(或沒有此方法),只有紀錄或查詢功能。
2.7.3 :023 > 65.count
Traceback (most recent call last):
(irb):23:in <main>': undefined method `count' for 65:Integer (NoMethodError)
2.7.3 :024 > "abc efg abc".count
count: wrong number of arguments (given 0, expected 1+) (ArgumentError)
2.7.3 :025 > "abc efg abc".count("a")
=> 2
2.7.3 :026 > "abc efg abc".count("ac")
=> 4
2.7.3 :027 > "abc efg abc".count("efg")
=> 3
想找符合efg
這樣連續的嗎? 請記得改用Regexp
。
2.7.3 :029 > "abc efg abc".match(/efg/)
=> #<MatchData "efg">
在Rails中,這三者被討論的部分在ActiveRecord
這個部分。length
與count
本質上不一樣的方法,在有不一樣用途時就該用不同的方法。
length
。
當如果已經load
過所有資料,單純查length
的話,就該用length
,而不是count
,以避免再對資料庫再一次進行query
。
2.7.3 :001 > user = User.first
User Load (1.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
2.7.3 :002 > user.articles.length
Article Load (1.1ms) SELECT "articles".* FROM "articles" WHERE "articles"."user_id" = $1 [["user_id", 1]]
=> 10
#載入過,不在需要去查詢。
2.7.3 :003 > user.articles.length
=> 10
但缺點也很明顯Article Load
進資料庫裡一個一個查。
count
。
當如果沒有load
過任何資料,那就請用count
來計算,count
會產生SQL裡的count()
函數,
不需要去load
。
2.7.3 :001 > articles = Article.all
Article Load (1.2ms) SELECT "articles".* FROM "articles" /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
2.7.3 :002 > articles.count
(2.9ms) SELECT COUNT(*) FROM "articles"
=> 10
2.7.3 :006 > articles.length
Article Load (0.8ms) SELECT "articles".* FROM "articles"
=> 10
資料少時,計算可能耗時,但資料多時,就明顯效能高很多,一筆一筆去讀取...這跟N+1
一樣恐怖呀。
但缺點是每次都會計算。
2.7.3 :002 > articles.count
(2.9ms) SELECT COUNT(*) FROM "articles"
=> 10
2.7.3 :003 > articles.count
(0.9ms) SELECT COUNT(*) FROM "articles"
=> 10
2.7.3 :004 > articles.count
(3.3ms) SELECT COUNT(*) FROM "articles"
=> 10
2.7.3 :005 > articles.count
(1.3ms) SELECT COUNT(*) FROM "articles"
=> 10
缺點中的優點是準確,每算一次都會檢查一次。
size
。
它不再是誰的別名了,它可以協助你視情況用length
或count
,應該說會幫你判斷要去讀取還是計數。定義類似如下。
def size
loaded? ? @jobs.length : count
# if loaded?
#@jobs.length
#else
#@jobs.count
#end
end
這是個讓你不用去苦惱用length
或count
的好幫手,但是使用這種自動功能,總有個小缺點,當你還是明確希望去count
或length
時,它可能給你執行相反的狀況,因為size
不是由你決定用哪個,而是看有沒有loaded
。
YA!明天最後一天啦!