170

Am I missing something in the Array documentation? I have an array which contains up to one object satisfying a certain criterion. I'd like to efficiently find that object. The best idea I have from the docs is this:

candidates = my_array.select { |e| e.satisfies_condition? }
found_it = candidates.first if !candidates.empty?

But I am unsatisfied for two reasons:

  1. That select made me traverse the whole array, even though we could have bailed after the first hit.
  2. I needed a line of code (with a condition) to flatten the candidates.

Both operations are wasteful with foreknowledge that there's 0 or 1 satisfying objects.

What I'd like is something like:

array.find_first(block)

which returns nil or the first object for which the block evaluates to true, ending the traversal at that object.

Must I write this myself? All those other great methods in Array make me think it's there and I'm just not seeing it.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303

4 Answers4

253

Either I don't understand your question, or Enumerable#find is the thing you were looking for.

esilver
  • 27,713
  • 23
  • 122
  • 168
Mladen Jablanović
  • 43,461
  • 10
  • 90
  • 113
  • 36
    No problem, I also often find annoying that ruby documentation doesn't list methods of all inherited or included classes and modules. – Mladen Jablanović Mar 04 '10 at 17:33
  • 9
    more info: `[1,2,3,5,2].find { |n| puts n; n == 2 } ` iteration stops once it finds the first element it matches. – rajuGT Apr 04 '17 at 13:23
112

use array detect method if you wanted to return first value where block returns true

[1,2,3,11,34].detect(&:even?) #=> 2

OR

[1,2,3,11,34].detect{|i| i.even?} #=> 2

If you wanted to return all values where block returns true then use select

[1,2,3,11,34].select(&:even?)  #=> [2, 34]
Sandip Ransing
  • 7,583
  • 4
  • 37
  • 48
29

Guess you just missed the find method in the docs:

my_array.find {|e| e.satisfies_condition? }
sepp2k
  • 363,768
  • 54
  • 674
  • 675
17

Do you need the object itself or do you just need to know if there is an object that satisfies. If the former then yes: use find:

found_object = my_array.find { |e| e.satisfies_condition? }

otherwise you can use any?

found_it = my_array.any?  { |e| e.satisfies_condition? }

The latter will bail with "true" when it finds one that satisfies the condition. The former will do the same, but return the object.

Taryn East
  • 27,486
  • 9
  • 86
  • 108