動かざることバグの如し

近づきたいよ 君の理想に

Rubyで配列からマッチした要素のindexを取得したい

Rubyの記事久々すぎて書き方忘れたw

環境

やりたいこと

タイトル通りだが、例えば a〜zの配列でaとkにマッチする要素を取得するときはselectでいける

array = ("a".."z").to_a
array.select{|item| item == "a" || item == "k"  }
# => ["a", "k"]

が、今回は要素そのものではなく要素が格納されている配列のインデックスが知りたい。

コード

意外にもRubyにネイティブなメソッドは用意されてないっぽい。そこで array.each_index.select をつかうことにした

array.each_index.select{|i| array[i] == "a" || array[i] == "k"  }
# => [0, 10]

each_indexでindexのEnumeratorオブジェクトを生成し、selectで評価していく方法。これが一番しっくり来た

他にも

色々あるっぽい。そこでベンチマークをとってみた。

require 'benchmark'

arr = 10000000.times.map{rand(1000)};

Benchmark.bm(7) do |x|
  x.report("arr.each_with_index.map:")   { arr.each_with_index.map { |a, i| a == 50 ? i : nil }.compact }
  x.report("arr.size-1 .select:") { (0..arr.size-1).select { |i| arr[i] == 50 } }
  x.report("arr.map.with_index:")  { arr.map.with_index {|a, i| a == 50 ? i : nil}.compact }
  x.report("arr.each_index.select:")  { arr.each_index.select{|i| arr[i] == 50} }
  x.report("arr.size.times.select:")  { arr.size.times.select {|i| arr[i] == 50} }
end

結果

              user     system      total        real
arr.each_with_index.map:  1.006136   0.055187   1.061323 (  1.062394)
arr.size-1 .select:  0.754916   0.000831   0.755747 (  0.757302)
arr.map.with_index:  0.879967   0.055472   0.935439 (  0.936816)
arr.each_index.select:  0.734339   0.000641   0.734980 (  0.735821)
arr.size.times.select:  0.732360   0.000913   0.733273 (  0.734476)

速度でなら arr.size.times.select が最強か。上で紹介した arr.each_index.selectも悪くなさそう。

参考リンク