17

Say I have a function that checks whether some operation is applicable to an instance of A and, if so, returns an instance of B or None:

   def checker[A,B]( a: A ) : Option[B] = ...

Now I want to form a new collection that contains all valid instances of B, dropping the None values. The following code seems to do the job, but there is certainly a better way:

   val as = List[A]( a1, a2, a3, ... )
   val bs = 
     as
     .map( (a) => checker(a) )    // List[A] => List[Option[B]]
     .filter( _.isDefined )       // List[Option[B]] => List[Option[B]]
     .map( _.get )                // List[Option[B]] => List[B]
dbc
  • 104,963
  • 20
  • 228
  • 340
Gregor Scheidt
  • 3,952
  • 4
  • 24
  • 28

2 Answers2

25

This should do it:

val bs = as.flatMap(checker)
Kim Stebel
  • 41,826
  • 12
  • 125
  • 142
  • Doesn't work for me for various reasons: 1. type inference, 2. List#flatMap expects a GenTraversableOnce. The correct is as.flatMap{a => checker[A, B](a)} – IttayD Sep 28 '11 at 09:42
  • I tested it with a function from `Int` to `Option[String]`. Worst case, you have to add some explicit types to it. – Kim Stebel Sep 28 '11 at 09:56
11

The answer above is correct, but if you can rewrite checker, I suggest you use PartialFunction and collect. PartialFunction is a function of type A=>B that is not necessary defined for all values of A. Here is a simple example:

scala> List(1, 2, 3, 4, "5") collect {case x : Int => x + 42}
res1: List[Int] = List(43, 44, 45, 46)

collect takes an instance of PartialFunction as argument and applies it to all elements of the collection. In our case the function is defined only for Ints and "5" is filtered. So, collect is a combination of map and filter, which is exactly your case.

E. Verda
  • 285
  • 1
  • 6
  • 1
    `collect` is indeed a combination of `filter` and then `map`, but it doesn't generally work in the other direction (`map` **then** `filter`), since you need to define the filter in the guard of the case statement. So it'd be great for replacing the OP's last two statements (with `.collect { case Some(x) => x }`), but if `checker` involves any sort of non-trivial calculation to decide whether to return a `Some` or a `None`, this might be difficult to write as a partial function. – Andrzej Doyle Sep 28 '11 at 10:23