I've had this situation occur a number of times in the library I'm writing, and I'm not particularly satisfied with the solutions I've come up with so far.
Let's say that I have an expensive function f that takes an item of type T and returns a value of type Option[U]. Now, suppose I have a collection of type T and I want to retrieve the first non-None value returned by f when performed across the elements of T without evaluating f for all elements of T if the value has already been found.
The only way I've come up with to do this is to wrap F into an Extractor Object, and use it with scala's collectFirst method.
For example:
object FMatch { def unapply(t : T) = f(t) }
collection.collectFirst{ case FMatch(result) => result }
This seems a little inelegant, and I'm not certain whether f is evaluated only once or twice for each result (I haven't tested this to find out yet). It seems like it would be useful to have a version of collectFirst that takes a parameter of type T => Option[U] instead of a PartialFunction1[T].
Is there a more elegant way to do this that I'm missing?
Use a view over the collection, to make it lazy and defer invocation of that function until the last possible moment (e.g. it won't be called at all for elements beyond the first match):
xs.view map {f(_)} collectFirst {case Some(x) => x}
or
xs.view map {f(_)} find {_.isDefined}
or in the point-free style, as per Alexey's response:
xs.view map {f} find {_.isDefined}
That should hopefully give you a couple of alternative ways to think about the problem more generally :)
Use this:
collection.toIterator.map(f).find(_.isDefined)
@annotation.tailrec
def first[A, B](as: Traversable[A], f: A => Option[B]): Option[B] =
if (as.isEmpty) None
else f(as.head) match {
case s @ Some(_) => s
case _ => first(as.tail, f)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With