Read more

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

Dominik Schöler
September 04, 2015Software engineer at makandra GmbH

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
Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

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.

Posted by Dominik Schöler to makandra dev (2015-09-04 10:29)