要素数が必ず1になる配列から値を取り出すという状況には、意外と遭遇すると思います・・・・いや、やっぱりしないかもしれません。例えばrailsだと・・・
# emailカラムはユニーク
User.where(email: 'sample@gmal.com').first
みたいなのです。find_by使えと突っ込まれそうですが、実際に業務の中でこのようなコードには頻繁に遭遇します(自分で書くことはありませんが・・・)。
私が現在扱っている自社メディアのコード内に限った話ですが、このような処理を行っている箇所では、必ずと言っていいほどfirstを使って値を取り出していました。
そのfirstに意味はあるのか
[1].first
上記の処理を日本語で説明するなら、"要素数が1の配列から、先頭の要素を取り出す"と言えると思います。ここでは先頭の要素を選択していますが、そもそも選択候補が1つしかないわけです。結果、メソッドの意味とコード中での目的が食い違うこととなり、コードの可読性を落としてしまいます。こういった処理は避けたほうがいい気がします。しかし、実際このような記述は、苦肉の策として理解できなくもありません。
そもそも何が言いたいのか
何が伝えたいのか分からなくなってきましたが、結局言いたいのは・・・
firstとかが出てくるコードには、何らかの技術的負債があるかもしれないということです。あくまでも私見であり、これらが効果的に使える場面も多々あるとは思います。
以下、おまけです。
仕方がない場合はどうするか
では、"要素数が1の配列から、値を取り出す"という行為がどうしても必要な場合はどうしましょう。例えばfirst以外にも代替案として以下のようなものがありますが・・・
[1].first
[1].last
[1].sample
[1].pop
[1][0]
いずれも目的とは意味が食い違います。
せめて処理が速いものを
どーせ無理なら少しでも速いものを選択したいです。というわけで、以下の処理を行い、処理速度を計測してみました。
# helloという文字列が入った要素数1の配列を2000000個持つ配列を宣言
array = Array.new(2000000).map { Array.new(1, 'hello')}
# 実行時間計測用
t = Time.now
array.each do |val|
val # ここを変化させて、それぞれ処理時間を計測する
end
p Time.now - t
上記のコードで、each内のvalを随時変更して、処理にかかった時間を計測してみました。10回の平均値をとっています。
コード | 実行時間 |
---|---|
val(要素を取り出さない) | 0.078 s |
val.first | 0.119 s |
val.last | 0.118 s |
val.sample | 0.125 s |
val[0] | 0.099 s |
当然ではありますが、val[0]が明らかに速いです。
また、タイプ数で比較すると、firstが5タイプ、lastが4タイプ、[0]が3タイプです。しかし、[0]はホームポジションから遠いため、少し打ちずらいですね。というわけで、タイピングの打ち易さから考えると、lastが最善に思えます(firstとlastに速度差はないですし)。
結局どうすればいいのか
処理が速いのは文句なしにval[0]の書き方です。打ち易いのはlastですかね。ですが、これがいいのかというと正直微妙です。そもそも、 "要素数が1と確定している配列から値を取り出す" という処理なんかするなって話ですが・・・
何か助言がありましたら、ぜひお願いします。