views:

144

answers:

2

This is something I encounter frequently, but I don't know the elegant way of doing. I have a collection of Foo objects. Foo has a method bar() that may return null or a Bar object. I want to scan the collection, calling each object's bar() method and stop on the first one returning an actual reference and return that reference from the scan.

Obviously:

foos.find(_.bar != null).bar

does the trick, but calls #bar twice.

+6  A: 

Working on the Stream[T] returned by Seq.projection is a nice trick

foos.projection map (_.bar) find (_.size > 0)

This will map the values needed to execute find.

In Scala 2.8 it's:

foos.view map (_.bar) find (_.size > 0)
Thomas Jung
+4  A: 

You can do it with any Iterable by using iterator (which evaluates lazily--it's called elements in 2.7). Try this out:

case class Foo(i: Int) {
  def bar = {
    println("Calling bar from Foo("+i+")")
    (if ((i%4)==0) "bar says "+i else null)
  }
}
val foos = List(Foo(1),Foo(2),Foo(3),Foo(4),Foo(5),Foo(6))
foos.iterator.map(_.bar).find(_!=null)
Rex Kerr
is there a difference between this and using a projection?
IttayD
Memoization of Stream[T] is the difference `val mapped = foos.elements.map(_.bar); mapped.find(_!=null);mapped.find(_!=null)` returns `Some(bar say 4)` and `None` where `val mapped = foos.projection.map(_.bar); mapped.find(_!=null);mapped.find(_!=null)` returns twice `Some(bar say 4)`.
Thomas Jung
`foos.view` in Scala 2.8 returns the same as `foos.projection` but evaluates the elements twice (does no memoization).
Thomas Jung
Use projections if you want to use the result multiple times (or some of the small set of methods available only on projections); otherwise, iterators are cheaper since they don't bother saving state.
Rex Kerr