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 web development

Do you need DevOps-experts?

Your development team has a full backlog? No time for infrastructure architecture? Our DevOps team is ready to support you!

  • We build reliable cloud solutions with Infrastructure as code
  • We are experts in security, Linux and databases
  • We support your dev team to perform
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)