Thursday, April 29, 2010

Filter with FlatMap (or collect)

I picked up this tip from one of Daniel Spiewak's tweets. He tweeted a pro tip that uses flatMap to create a filtered list:
  1. list flatMap {
  2.   case st: String => Some(st)
  3.   case _ => None
  4. }

At a glance one might wonder why not simply use list.filter{_.isInstanceOf[String]}. The difference is that the flatMap will return a List[String].

However Scala 2.8 offers the collect method for doing a similar thing.
  1. def strings(list: List[Any]) = list flatMap {
  2.   case st: String => Some(st)
  3.   case _ => None
  4. }
  5. // returned list is a List[String]
  6. scala> strings("hi" :: 1 :: "world" :: 4 :: Nil)
  7. res11: List[String] = List(hi, world)
  8. // returned list is a List[Any] (not as useful)
  9. scala> "hi" :: 1 :: "world" :: 4 :: Nil filter {_.isInstanceOf[String]}
  10. res12: List[Any] = List(hi, world)
  11. // collect returns List[String]
  12. scala> "hi" :: 1 :: "world" :: 4 :: Nil collect {case s:String => s}           
  13. res13: List[String] = List(hi, world)

4 comments:

  1. How about this?

    list flatMap {x=>Option(x.asInstanceOf[String])}

    ReplyDelete
  2. Madoc, that leads to ClassCastException

    ReplyDelete
  3. hmm...
    list flatMap {x => catching[String](classOf[ClassCastException]) {
    Some(x.asInstanceOf[String])
    }

    ReplyDelete
  4. You can do the same thing with collect, but even simpler because it will ignore anything that doesn't have a case:


    scala> val l = List(1,2,3,4)
    l: List[Int] = List(1, 2, 3, 4)


    scala> l.collect { case 1 => 1 }
    res1: List[Int] = List(1)

    ReplyDelete