tags:

views:

174

answers:

3

I'm looking for a collect method in scala 2.7 but I can't seem to find an applicable call. Is there something equivalent to collect that I can use in scala?

To be clear I'm looking to filter elements from a list and map those filtered to a new type.

+7  A: 

You can use flatMap (the full method signature in 2.7 is def flatMap[B](f : (A) => Iterable[B]) : List[B] ). It is declared on both Iterable and Iterator (with slightly different signatures):

scala> val l = List("1", "Hello", "2")
l: List[java.lang.String] = List(1, Hello, 2)

scala> val ints = l.flatMap { s => try { Some(s.toInt) } catch { case _ => None } }
ints: List[Int] = List(1, 2)

In the above example I'm taking advantage of an explicit conversion option2iterable in Predef. It's declared in 2.8 on TraversableLike:

def flatMap[B, That](f: A => Traversable[B])(implicit bf: CanBuildFrom[Repr, B, That]): That
oxbow_lakes
+1  A: 
scala> List(-1,-2, 1,2,3).filter{i => (i % 2) == 0}.map{i => i / 2} 

line10: scala.List[scala.Int] = List(-1,1)

You need to seperate out the two calls

From a very useful blog

Chris
You don't need to separate out the calls at all
oxbow_lakes
+7  A: 

Using flatMap/Option, as described by Chris is the way I do it usually, but there is a more involved alternative that is, sometimes, more pleasing to the eyes:

class Filter[A](f: Any => Option[A]) {
  def unapply(a: Any) = f(a)
}

object Filter {
  def apply[A](f: Any => Option[A]) = new Filter(f)
}

val evens = Filter {
  case n: Int if n % 2 == 0 => Some(n)
  case _ => None
}

Usage:

scala> for (evens(n) <- List.range(0,10)) yield n
res0: List[Int] = List(0, 2, 4, 6, 8)
Daniel
You can call me *Chris*, Daniel!
oxbow_lakes
Or the one-liner: `for (i <- 1 to 10 if i % 2 == 0) yield i * 2`
retronym
@retronym True enough, though there are certain pattern matches which are not possible using a filter. For instance (perhaps, uniquely), type checking.
Daniel
Technically it can still be done, although it's not particularly intuitive: `for { a@(x: Int) <- Seq(1, "1", 2, "2") if a % 2 == 0} yield a`. As you know, #900 prevents `for { x: Int <- ...`. Oddly, `for { a@(_: Int) <-` doesn't work.
retronym
I never considered that alternative. I wouldn't have expected it to work either. Learn something every day. :-)
Daniel