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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13659696/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 05:33:37  来源:igfitidea点击:

Find indices of elements that match a given condition

rubyarrays

提问by Misha Moroshko

Given an array, how can I find all indices of elements those match a given condition?

给定一个数组,如何找到与给定条件匹配的元素的所有索引?

For example, if I have:

例如,如果我有:

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

To find all indices where the item is x, I could do:

要查找项目所在的所有索引x,我可以执行以下操作:

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

or

或者

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

Is there a nicer way to achieve this?

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

回答by steenslag

Ruby 1.9:

红宝石 1.9:

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

Code

代码

回答by AGS

Another way:

其它的办法:

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

EDIT:

编辑:

Not sure if this is even needed, but here they are.

不确定是否需要这样做,但它们就在这里。

Benchmarks:

基准:

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)

回答by Sergio Tulentsev

A slight improvement over your each_with_index.mapline

对您的each_with_index.map线路略有改进

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

回答by peter

This methods is a bit longer but double as fast

这种方法有点长,但速度加倍

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]

Here the benchmark of AGS campared with this solution

这里是 AGS 的基准测试与此解决方案的对比

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)

回答by Mark Reed

Not sure if you consider this an improvement or not, but using (map+ compact) as a filter feels very clunky to me. I would use select, since that's what it's for, and then just grab the part of the result I care about:

不确定您是否认为这是一种改进,但是使用 ( map+ compact) 作为过滤器对我来说感觉很笨拙。我会使用select,因为这就是它的用途,然后只获取我关心的结果部分:

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

回答by Junichi Ito

I defined Array#index_allwhich behaves like Array#indexbut returns all matched indices. This method can take an argument and block.

我定义了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