2

I'm looking for a way to do the following in Ruby in a cleaner way:

class Array
  def find_index_with_offset(offset, &block)
    [offset..-1].find &block
  end
end

offset = array.find_index {|element| element.meets_some_criterion?}
the_object_I_want = 
    array.find_index_with_offset(offset+1) {|element| element.meets_another_criterion?}

So I'm searching a Ruby array for the index of some object and then I do a follow-up search to find the first object that matches some other criterion and has a higher index in the array. Is there a better way to do this?

What do I mean by cleaner: something that doesn't involve explicitly slicing the array. When you do this a couple of times, calculating the slicing indices gets messy fast. I'd like to keep operating on the original array. It's easier to understand and less error-prone.

NB. In my actual code I haven't monkey-patched Array, but I want to draw attention to the fact that I expect I'm duplicating existing functionality of Array/Enumerable


Edits

  • Fixed location of offset + 1 as per Mladen Jablanović's comment; rewrite error
  • Added explanation of 'cleaner' as per Mladen Jablanović's comment
3
  • What do you mean by "cleaner"? What's bothering you with your solution? The fact that [] creates a new array? Or just the aesthetics? Commented Mar 29, 2012 at 13:02
  • BTW, I think you should search within [offset..-1], and pass (offset+1) to the method call later instead. It's more logical that way IMO. Commented Mar 29, 2012 at 13:05
  • Simple loop is cleaner if you want to avoid slicing Commented Mar 29, 2012 at 13:39

1 Answer 1

3

Cleaner is here obviously subjective matter. If you aim for short, I don't think you could do better than that. If you want to be able to chain multiple such finds, or you are bothered by slicing, you can do something like this:

module Enumerable
  def find_multi *procs
    return nil if procs.empty?
    find do |e|
      if procs.first.call(e)
        procs.shift
        next true if procs.empty?
      end
      false
    end
  end
end


a = (1..10).to_a
p a.find_multi(lambda{|e| e % 5 == 0}, lambda{|e| e % 3 == 0}, lambda{|e| e % 4 == 0})
#=> 8

Edit: And if you're not concerned with the performance you could do something like:

array.drop_while{|element|
  !element.meets_some_criterion?
}.drop(1).find{|element|
  element.meets_another_criterion?
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.