How to iterate over an Enumerable, returning the first truthy result of a block ("map-find")

Updated . Posted . Visible to the public. Repeats.

Ruby has Enumerable.find(&block), which returns the first item in the collection for which the block evaluates to true.

first_post_with_image = posts.find do |post|
  post.image
end

However, sometimes it's not the item you're interested in, but some value depening on it – e.g. the value the block evaluated to. You could first map the collection and then take the first truthy value, but this way you need to process the whole collection twice:

first_image_url = posts.map(&:image).find(&:present?).url

If the mapping is a costly operation or has undesirable side effects, you need to do it in a single iteration instead.

Single iteration solution

first_image_url = posts.find do |post|
  break post.image.url if post.image.present?
end

Explanation

As you know, #find loops through the collection, yielding the block with each item. Calling break with an argument will exit the whole block evaluation, returning whatever argument was given to break.

While the condition (post.image.present?) is false, the block returns nil and #find will try the next item in the collection. If the condition is never true, #find will return nil.

Dominik Schöler
Last edit
Judith Roth
License
Source code in this card is licensed under the MIT License.
Posted by Dominik Schöler to makandra dev (2015-09-04 08:29)