Ruby:在数组中查找下一个匹配的索引,或者使用offset查找

时间:2015-11-30 12:08:42

标签: arrays ruby

我希望首次在Array#find_index { |item| block }匹配后找到更多匹配项。如何搜索第二场比赛,第三场比赛等的索引?

换句话说,对于pos,我希望将Regexp#match(str, pos)参数等效为Array#find_index。然后我可以保持当前位置索引以继续搜索。

我无法使用Enumerable#find_all,因为我可能会在调用之间修改数组(在这种情况下,我还会调整当前位置索引以反映修改)。我不想复制数组的一部分,因为这会增加我算法的computational complexity。我想这样做而不复制数组:

new_pos = pos + array[pos..-1].find_index do |elem|
    elem.matches_condition?
end

以下是不同的问题。他们只询问数组中的第一个匹配,加上一个:

以下问题更接近,但仍然无法帮助我,因为我需要在继续下一个之前处理第一个匹配(这种方式也与修改冲突):

5 个答案:

答案 0 :(得分:2)

更简单的方法就是:

new_pos = pos
while new_pos < array.size and not array[new_pos].matches_condition?
    new_pos += 1
end
new_pos = nil if new_pos == array.size

事实上,我认为这可能比我的其他答案更好,因为它更难以出错,并且没有机会从周围的代码中引入未来的阴影问题。但是,它仍然很笨拙。

如果条件更复杂,那么你最终需要做这样的事情:

new_pos = pos
# this check is only necessary if pos may be == array.size
if new_pos < array.size
    prepare_for_condition
end
while new_pos < array.size and not array[new_pos].matches_condition?
    new_pos += 1
    if new_pos < array.size
        prepare_for_condition
    end
end
new_pos = nil if new_pos == array.size

或者,上帝保佑,begin ... end while循环(虽然你遇到了new_pos的初始值的麻烦):

new_pos = pos - 1
begin
    new_pos += 1
    if new_pos < array.size
        prepare_for_condition
    end
end while new_pos < array.size and not array[new_pos].matches_condition?
new_pos = nil if new_pos == array.size

这可能看起来很糟糕。但是,假设prepare_for_condition是以不同方式进行调整的东西。这些调整最终会被重构;然而,到那个时候,重构代码的输出也将最终以不同于旧的重构代码的小方式进行调整,但似乎还没有证明自己的重构是合理的 - 依此类推。偶尔,有人会忘记改变这两个地方。这似乎是病态的;然而,在编程中,众所周知,病态病例的习惯是经常发生。

答案 1 :(得分:2)

这是一种可以做到的方法。我们可以在Array类中定义一个新方法,它允许我们找到与给定条件匹配的索引。条件可以指定为返回布尔值的块。

新方法返回Enumerator,以便我们获得许多Enumerator方法的好处,例如nextto_a等。

ary = [1,2,3,4,5,6]

class Array
  def find_index_r(&block)
    Enumerator.new do |yielder| 
      self.each_with_index{|i, j| yielder.yield j if block.call(i)}
    end
  end
end

e = ary.find_index_r { |r| r % 2 == 0 }

p e.to_a  #=> [1, 3, 5]
p e.next  
#=> 1
p e.next  
#=> 3
ary[2]=10 
p ary
#=> [1, 2, 10, 4, 5, 6]
p e.next  
#=> 5
e.rewind
p e.next  
#=> 1
p e.next  
#=> 2

注意:我在Array课程中添加了一个新方法用于演示目的。解决方案可以轻松调整,无需猴子修补

答案 2 :(得分:1)

当然,一种方法是:

ActionBar

但是,这很笨拙,容易出错。例如,您可能忘记添加new_pos = pos + (pos...array.size).find_index do |index| elem = array[index] elem.matches_condition? end 。此外,您必须确保pos不会影响某些内容。这两者都可能导致难以追踪的错误。

我发现很难相信elemArray#find_index的索引参数仍然没有成为语言。但是,我注意到版本1.9之前不存在Array#index,这同样令人惊讶。

答案 3 :(得分:0)

假设

arr = [9,1,4,1,9,36,25]
findees = [1,6,3,6,3,7]
proc = ->(n) { n**2 }

并且对于n中的每个元素findees,我们需要m的{​​{1}}的第一个不匹配元素arr的索引。例如,如果which proc[n] == m,则为n=3,因此proc[3] #==> 9中的第一个匹配索引为arr。对于0中的下一个n=3findees中的第一个不匹配匹配位于索引arr

我们可以这样做:

4

我们可以将其概括为一个方便的arr = [9,1,4,1,9,36,25] findees = [1,6,3,6,3,7] proc = ->(n) { n**2 } h = arr.each_with_index.with_object(Hash.new { |h,k| h[k] = [] }) { |(n,i),h| h[n] << i } #=> {9=>[0, 4], 1=>[1, 3], 4=>[2], 36=>[5], 25=>[6]} findees.each_with_object([]) { |n,a| v=h[proc[n]]; a << v.shift if v } #=> [1, 5, 0, nil, 4, nil] 方法,如下所示:

Array

答案 4 :(得分:0)

我的方法与其他方法没有太大区别,但可能打包后的清洁器在语法上与 Array#find_index 相似。这是紧凑形式。

def find_next_index(a,prior=nil)
    (((prior||-1)+1)...a.length).find{|i| yield a[i]}
end

这是一个简单的测试用例。

test_arr = %w(aa ab ac ad)
puts find_next_index(test_arr){|v| v.include?('a')}
puts find_next_index(test_arr,1){|v| v.include?('a')}
puts find_next_index(test_arr,3){|v| v.include?('a')}

# evaluates to:
#     0
#     2
#     nil

当然,只需稍加重写,您就可以将其修补到 Array 类中