Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a built-in more elegant way of filtering-and-mapping a collection by element type?

If I want to narrow, say, an Iterable[A] for all elements of a particular type (e.g. String) I can do:

as filter { _.isInstanceOf[String] }

However, it's obviously desirable to use this as an Iterable[String] which can be done via a map:

as filter { _.isInstanceOf[String] } map { _.asInstanceOf[String] }

Which is pretty ugly. Of course I could use flatMap instead:

as flatMap[String] { a => 
  if (a.isInstanceOf[String]) 
    Some(a.asInstanceOf[String]) 
  else
    None
}

But I'm not sure that this is any more readable! I have written a function, narrow, which can be used via implicit conversions:

as.narrow(classOf[String])

But I was wondering if there was a better built-in mechanism which I have overlooked. Particularly as it would be nice to be able to narrow a List[A] to a List[String], rather than to an Iterable[String] as it will be with my function.

like image 627
oxbow_lakes Avatar asked Dec 13 '22 02:12

oxbow_lakes


1 Answers

The Scala syntax sugar for isInstanceOf / asInstanceOf is pattern matching:

as flatMap { case x: String => Some(x); case _ => None }

Because that uses flatMap, it should usually return the same collection you had to begin with.

On Scala 2.8, there's an experimental function that does that kind of pattern, defined inside the object PartialFunction. So, on Scala 2.8 you can do:

as flatMap (PartialFunction.condOpt(_ : Any) { case x: String => x })

Which looks bigger mostly because I did not import that function first. But, then again, on Scala 2.8 there's a more direct way to do it:

as collect { case x: String => x }
like image 195
Daniel C. Sobral Avatar answered May 23 '23 05:05

Daniel C. Sobral