【问题标题】:Find indices of elements that match a given condition查找与给定条件匹配的元素的索引
【发布时间】:2012-11-19 12:40:57
【问题描述】:

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

例如,如果我有:

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

要查找项目为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​​ 不太熟悉...您可以使用 regexp
  • 我猜逻辑和语法 :) 不确定正则表达式,因为项目不是必需的字符串。
  • 这是我认为 Python 的列表推导式读起来确实更好的少数情况之一:[i for i,a in enumerate(arr) if a == 'x']
  • 我很烦Enumerable#find_indices(elem) 不在 ruby​​ 标准库中。不知何故,这将提出一个合理的功能请求。

标签: ruby arrays


【解决方案1】:

另一种方式:

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)

【讨论】:

  • 赞成最合理的实施。其他任何事情都更复杂。
  • 我会说接受的答案更直接; arr.size.times == arr.each_index。只有一个方法调用而不是两个。但根据此处添加的基准,显然慢了一点?
【解决方案2】:

比您的 each_with_index.map 行略有改进

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

【讨论】:

    【解决方案3】:

    Ruby 1.9:

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

    Code

    【讨论】:

    • 谢谢。在此之前我只找到了arr.enum_for(:each_with_index).collect {|item,index| item=='x' ? index : nil }.delete_if {|i| i.nil? }
    • @6ftDan delete_if {|i| i.nil? } == compact.
    • 出于好奇,在这种情况下 p 做了什么?
    • @user5783745 p 以“开发人员友好”的方式将表达式的结果打印到屏幕上。在这种情况下:[0, 2, 6]
    • @user5783745 my_var = p arr.each_index.select{|i| arr[i] == 'x'} 使用或不使用p
    【解决方案4】:

    这种方法有点长,但速度是原来的两倍

    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 的基准与此解决方案相匹配

    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)
    

    【讨论】:

    • 请注意,这仅在将索引与参数一起使用时才有效,而不是与块一起使用。对于一个区块,接受的解决方案仍然是唯一的方法。
    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        猜你喜欢
        • 2015-05-13
        • 1970-01-01
        • 2015-01-03
        • 1970-01-01
        • 1970-01-01
        • 2021-11-19
        • 2022-01-16
        • 2013-03-28
        • 1970-01-01
        相关资源
        最近更新 更多