LoginSignup
1
0

reject(&:empty?)と正規表現などを駆使して配列をいじる場合、grepが良さそう

Last updated at Posted at 2024-03-20

課題

数字の文字列の配列として返却されるbook_idsを、整数の配列に変換し、さらに重複を除去したい。

book_ids.rb
["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 受験記 + 参考リンク集

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0