I wish to find a match within a List and return values dependant on the match. The CollectFirst works well for matching on the elements of the collection but in this case I want to match on the member swEl of the element rather than on the element itself.
abstract class CanvNode (var swElI: Either[CSplit, VistaT])
{
private[this] var _swEl: Either[CSplit, VistaT] = swElI
def member = _swEl
def member_= (value: Either[CSplit, VistaT] ){ _swEl = value; attach}
def attach: Unit
attach
def findVista(origV: VistaIn): Option[Tuple2[CanvNode,VistaT]] = member match
{
case Right(v) if (v == origV) => Option(this, v)
case _ => None
}
}
def nodes(): List[CanvNode] = topNode :: splits.map(i => List(i.n1, i.n2)).flatten
//Is there a better way of implementing this?
val temp: Option[Tuple2[CanvNode, VistaT]] =
nodes.map(i => i.findVista(origV)).collectFirst{case Some (r) => r}
Do I need a View on that, or will the collectFirst method ensure the collection is only created as needed?
It strikes me that this must be a fairly general pattern. Another example could be if one had a List member of the main List’s elements and wanted to return the fourth element if it had one. Is there a standard method I can call? Failing that I can create the following:
implicit class TraversableOnceRichClass[A](n: TraversableOnce[A])
{
def findSome[T](f: (A) => Option[T]) = n.map(f(_)).collectFirst{case Some (r) => r}
}
And then I can replace the above with:
val temp: Option[Tuple2[CanvNode, VistaT]] =
nodes.findSome(i => i.findVista(origV))
This uses implicit classes from 2.10, for pre 2.10 use:
class TraversableOnceRichClass[A](n: TraversableOnce[A])
{
def findSome[T](f: (A) => Option[T]) = n.map(f(_)).collectFirst{case Some (r) => r}
}
implicit final def TraversableOnceRichClass[A](n: List[A]):
TraversableOnceRichClass[A] = new TraversableOnceRichClass(n)
As an introductory side node: The operation you’re describing (return the first
Someif one exists, andNoneotherwise) is the sum of a collection ofOptions under the “first” monoid instance forOption. So for example, with Scalaz 6:Alternatively you could put something like this in scope:
And skip the
.map(_.fst)part. Unfortunately neither of these approaches is appropriately lazy in Scalaz, so the entire stream will be evaluated (unlike Haskell, wheremconcat . map (First . Just) $ [1..]is just fine, for example).Edit: As a side note to this side note: apparently Scalaz does provide a
sumrthat’s appropriately lazy (for streams—none of these approaches will work on a view). So for example you can write this:And not wait forever for your answer, just like in the Haskell version.
But assuming that we’re sticking with the standard library, instead of this:
I’d write the following, which is more or less equivalent, and arguably more idiomatic:
For example, suppose we have a list of integers.
We can make this lazy and
mapa function with a side effect over it to show us when its elements are accessed:Now we can
flatMapanOption-returning function over the resulting collection and useheadOptionto (safely) return the first element, if it exists:So clearly this stops when we hit a non-empty value, as desired. And yes, you’ll definitely need a view if your original collection is strict, since otherwise
headOption(orcollectFirst) can’t reach back and stop theflatMap(ormap) that precedes it.In your case you can skip
findVistaand get even more concise with something like this:Whether you find this clearer or just a mess is a matter of taste, of course.