Ruby's Enumerable#find Method

Today we dive into Enumerable's #find/#detect method and how to use it effectively.

Ruby's Enumerable#find Method

In the latest attempt at getting back into Ruby and Rails development, I started going through the Ruby documentation to learn about all the various helper methods that are available. Today, I present #find.

Ruby's documentation describes #find as:

Passes each entry in enum to block. Returns the first for which block is not false.  If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.

In short, find the first item in the Enumerable that passes the condition in the block. It can also be called via #detect, if you're feeling fancy. Let's look at an example.

# Find the first number in the list that occurs an odd number of times
arr = [1, 1, 2, 2, 3, 4, 4]

# Returns 3, as this is the only value that exists only once
arr.detect {|i| arr.count(i).odd? }

# Returns 1, as it's the first number that this condition passes for
arr.detect {|i| arr.count(i).even? }

# Returns nil, as nothing matches this condition
arr.detect {|i| arr.count(i) == 3 }

In the above cases, the first block is all that is provided. An optional parameter of a proc or lambda can also be provided if you want to override the nil functionality. For example, you could populate the errors flash in Rails if nothing matched.

arr.detect(-> {flash[:errors] << "Could not find result"; nil}) do |i|
  arr.count(i) == 3
# flash[:errors] now has "Could not find result" at the end

Depending on your use case, you might be more interested in #find_all, which grabs every item that matches your condition, or #find_index, which grabs the index or key of the first matching item, rather than the value. And because this is on the Enumerable module, you have plenty of options for where you can use this, including Arrays and Hashes. Depending on what you're running, you may have other options which you can find with:

ObjectSpace.each_object(Class).select {|x| x.included_modules.include? Enumerable}