iT邦幫忙

2021 iThome 鐵人賽

DAY 29
0
自我挑戰組

初級紅寶石魔法師心得分享。系列 第 29

D-1, length、size & count

很簡單的一個問題,但其實了解後蠻好玩的。

When google translate

length : 長度。
size : 尺寸。
count : 計數。


When Ruby。

size&length
在陣列與Hash這種長條狀的資料上,sizelength基本上一樣,也可說是sizelength的別名,陣列與Hash在記憶體中,就是一條長條狀的儲存方式,所以長度本就比尺寸來形容更恰當。
length比size要多打2個字母啦。

2.7.3 :009 > [1, 2, 3, 4].size
 => 4
2.7.3 :010 > [1, 2, 3, 4].length
 => 4

或者該說,記憶體型態上如果是這種長條型的,lengthsize沒有差別,都是能瞭解長度的同名方法而已了。

例如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
lengthsizecount都是計數方法,但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">

when Rails

在Rails中,這三者被討論的部分在ActiveRecord這個部分。
lengthcount本質上不一樣的方法,在有不一樣用途時就該用不同的方法。

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
它不再是誰的別名了,它可以協助你視情況用lengthcount,應該說會幫你判斷要去讀取還是計數。定義類似如下。

def size
  loaded? ? @jobs.length : count
  # if loaded?
      #@jobs.length
    #else
      #@jobs.count
    #end
end

這是個讓你不用去苦惱用lengthcount的好幫手,但是使用這種自動功能,總有個小缺點,當你還是明確希望去countlength時,它可能給你執行相反的狀況,因為size不是由你決定用哪個,而是看有沒有loaded

YA!明天最後一天啦!


上一篇
D-2.Line_pay_api 串接(三) Rails 串接
下一篇
D-Day -- 我覆蓋魔法牌 結束這回合!
系列文
初級紅寶石魔法師心得分享。30

尚未有邦友留言

立即登入留言