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_._