課題
数字の文字列の配列として返却されるbook_idsを、整数の配列に変換し、さらに重複を除去したい。
["1", "2", "2"]
アプローチ1
rejectメソッドを使用して空の要素を除去し、selectメソッドで数値のみを含む要素を選択し、最後にmapメソッドで整数に変換し、uniqメソッドで重複を除去する方法。
book_ids = book_ids.reject(&:empty?).select { |id| id.match?(/^\d+$/) }.map(&:to_i).uniq
こんな感じです。
ただ、このままだとパッと見何をしているかわからず、もう少し工夫できそうです。
使えそうなメソッドがないか探し、Enumerable#grepが良さそうだなと思ったので、正規表現をそのまま活用して良い感じにできないか考えてみました。
https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/grep.html
アプローチ2
grepメソッドを用いて数値のみを含む要素を選択し、その後mapメソッドで整数に変換し、uniqメソッドで重複を除去しました。
book_ids = book_ids.grep(/^\d+$/).map(&:to_i).uniq
マッチしなければ空の要素も一緒に排除されるので、こんな感じにすると一行でも読みやすくて良いと思いました。
測定
require 'benchmark'
book_ids = (1..100000).to_a.map(&:to_s)
Benchmark.bm do |x|
x.report("reject & select:") do
book_ids.reject(&:empty?).select { |id| id.match?(/^\d+$/) }.map(&:to_i).uniq
end
x.report("grep:") do
book_ids.grep(/^\d+$/).map(&:to_i).uniq
end
end
測定結果は見づらかったので、ChatGPTくんに頼んで表にしてもらいました。
方法 | 実行時間 (秒) |
---|---|
reject & select | 0.02796 |
grep | 0.01484 |
若干アプローチ2の方が速かったですね。
余談
他にも、重複があらかじめ多いとわかっている場合はuniq
を先にするなどの工夫ができそうです。
また、grepは次のような機能を持っています。
ブロックとともに呼び出された時には条件の成立した要素に対してそれぞれブロックを評価し、その結果の配列を返します。マッチする要素がひとつもなかった場合は空の配列を返します。
ですから、mapの部分もこれでできそうですね。ただ、自分はアプローチ2のままの方が読みやすかったので採用しました。
間違いやより良い方法があれば、ぜひアドバイスいただけると嬉しいです。
EnumerableはGoldの時によく出てきたので、こちらのリンクも掲載します。
【2023年12月版】Ruby Gold v3 受験記 + 参考リンク集