Daniel Doubrovkine bio photo

Daniel Doubrovkine

aka dB., @awscloud, former CTO @artsy, +@vestris, NYC

Email Twitter LinkedIn Github Strava
Creative Commons License

Given an Enumerable, you can detect a value that matches a condition. But what if you want the result of the evaluation? You can now use _Enumerable##detect_value _from the enumerable-detect-value gem.

Consider an example where you have an expensive Geocoder.search operation and a list of addresses, two of which are fake. The function returns nil for a fake address. We would like to find the geo-location of the first real address.

addresses = [
 '221B Baker Street, London, UK', # Sherlock Holmes
 '1428 Elm Street, Springwood, Ohio', # Nightmare on Elm Street
 '350 5th Ave, New York, NY' # Empire State Building
]

first_real_address = addresses.detect do |address|
 Geocoder.search(address)
end

first_real_address # 350 5th Ave, New York, NY

We would now have to call Geocoder.search on first_real_address twice.

Instead, using detect_value you can return the geo-location of the first real address.

first_geo_location = addresses.detect_value do |address|
 Geocoder.search(address)
end

first_geo_location # lat: 40.74830, lng: -73.98554

The implementation for detect_value is straightforward.

module Enumerable
 # Returns the first result of the block that is not nil.
 def detect_value(&block)
   each do |el|
     result = yield el
     return result if result
   end
   nil
 end
end

I don’t think this can this be done with the current Ruby standard library without introducing a temporary variable, and Enumerable##Lazy won’t help_._