Daniel Doubrovkine bio photo

Daniel Doubrovkine

aka dB., @Microsoft, ex-@ShopifyEng, @OpenSearchProj, @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_._