查找与给定条件匹配的元素的索引
给定一个数组,我怎么能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