查找与给定条件匹配的元素的索引

给定一个数组,我怎么能find符合给定条件的元素的所有索引?

例如,如果我有:

arr = ['x', 'o', 'x', '.', '.', 'o', 'x'] 

要find项目是x所有索引,我可以这样做:

 arr.each_with_index.map { |a, i| a == 'x' ? i : nil }.compact # => [0, 2, 6] 

要么

 (0..arr.size-1).select { |i| arr[i] == 'x' } # => [0, 2, 6] 

有没有更好的方法来实现这一点?

Ruby 1.9:

 arr = ['x', 'o', 'x', '.', '.', 'o', 'x'] p arr.each_index.select{|i| arr[i] == 'x'} # =>[0, 2, 6] 

其他方式:

 arr.size.times.select {|i| arr[i] == 'x'} # => [0, 2, 6] 

编辑:

不知道这是甚至需要,但他们在这里。

基准:

 arr = 10000000.times.map{rand(1000)}; Benchmark.measure{arr.each_with_index.map { |a, i| a == 50 ? i : nil }.compact} 2.090000 0.120000 2.210000 ( 2.205431) Benchmark.measure{(0..arr.size-1).select { |i| arr[i] == 50 }} 1.600000 0.000000 1.600000 ( 1.604543) Benchmark.measure{arr.map.with_index {|a, i| a == 50 ? i : nil}.compact} 1.810000 0.020000 1.830000 ( 1.829151) Benchmark.measure{arr.each_index.select{|i| arr[i] == 50}} 1.590000 0.000000 1.590000 ( 1.584074) Benchmark.measure{arr.size.times.select {|i| arr[i] == 50}} 1.570000 0.000000 1.570000 ( 1.574474) 

比你的each_with_index.map行略有改进

 arr.map.with_index {|a, i| a == 'x' ? i : nil}.compact # => [0, 2, 6] 

这个方法有点长,但是速度要快一倍

 class Array def find_each_index find found, index, q = -1, -1, [] while found found = self[index+1..-1].index(find) if found index = index + found + 1 q << index end end q end end arr = ['x', 'o', 'x', '.', '.', 'o', 'x'] p arr.find_each_index 'x' # [0, 2, 6] 

这里AGS的基准与这个解决scheme进行了对决

 arr = 10000000.times.map{rand(1000)}; puts Benchmark.measure{arr.each_with_index.map { |a, i| a == 50 ? i : nil }.compact} puts Benchmark.measure{(0..arr.size-1).select { |i| arr[i] == 50 }} puts Benchmark.measure{arr.map.with_index {|a, i| a == 50 ? i : nil}.compact} puts Benchmark.measure{arr.each_index.select{|i| arr[i] == 50}} puts Benchmark.measure{arr.size.times.select {|i| arr[i] == 50}} puts Benchmark.measure{arr.find_each_index 50} # 1.263000 0.031000 1.294000 ( 1.267073) # 0.843000 0.000000 0.843000 ( 0.846048) # 0.936000 0.015000 0.951000 ( 0.962055) # 0.842000 0.000000 0.842000 ( 0.839048) # 0.843000 0.000000 0.843000 ( 0.843048) # 0.405000 0.000000 0.405000 ( 0.410024) 

不知道如果你认为这是一个改进或不,但使用( map + compact )作为一个filter感觉非常笨重。 我会使用select ,因为这就是它,然后只是抓住我关心的结果的一部分:

 arr.each_with_index.select { |a,i| a == 'x' }.map &:last 

我定义了Array#index_all ,其行为与Array#index类似,但是返回所有匹配的索引。 这个方法可以带参数和块。

 class Array def index_all(obj = nil) if obj || block_given? proc = obj ? ->(i) { self[i] == obj } : ->(i) { yield self[i] } self.each_index.select(&proc) else self.each end end end require 'test/unit' class TestArray < Test::Unit::TestCase def test_index_all arr = ['x', 'o', 'x', '.', '.', 'o', 'x'] result = arr.index_all('x') assert_equal [0, 2, 6], result arr = [100, 200, 100, 300, 100, 400] result = arr.index_all {|n| n <= 200 } assert_equal [0, 1, 2, 4], result end end