How to filter for Unit


#1

Hey Guys,

In the following snippet, I’ve created a list with 2 unit types… how do I filter those out?

val lst = List(1, 2, 3)
val l = lst.map( x => {
  if (x > 2)
    x
})

l.foreach( println )

()
()
3

I would like to filter l so that removes the Units (ie: () ). Any suggestions would be appreciated.


#2

Really, you don’t want to get into that situation in the first place. If you look at the actual type signatures you’re winding up with (see here), you’ll see that you’re starting with a List[Int] (which is good), but then the map is creating a List[Any] (which is almost always bad). The lesson here is that explicit type ascriptions are your friend – while Scala lets you leave them off most of the time, they’re great for debugging.

The answer you’re looking for is easy, fortunately: use filter instead of map, as shown here.


#3

Thanks Justin, that works. What would you suggest if the snippet was tweaked to:

val lst = List(1, 2, 3)
val l = lst.map( x => {
  if (x > 2)
    x * 2
})

l.foreach( println )

()
()
6

#4

I would write it as follows. It’s very clear and fast enough for me 99% of the time.

lst.filter(_ > 2).map(_ * 2)

You can do it in a single pass like this, but I find it harder to understand at a glance.

lst.collect { case x if x > 2 => x * 2 }

You can also hop into a lazy structure like Iterator to fuse the operations together, which I rarely do but it’s another option.

lst.iterator.filter(_ > 2).map(_ * 2).toList

#5

Here is one way to just get the 3 back:

 val lst = List(1, 2, 3).map {
   case z if z > 2 => Some(z)
   case _ => None 
 }.flatMap(p => p).foreach(println)

#6

Actually, I see someone put a better approach: the collect function.


#7

That was very informative. Thank you for the detailed response, Rob.


#8

What @Justin du coeur said. But in case some one really wants to filter out Units, no problem (you can inline myUnit, but get some warnings):

**Welcome to Scala 2.12.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_191).
Type in expressions for evaluation. Or try :help.

val myUnit = ()
myUnit: Unit = ()
List(1, 2, 3, (), (), 4, (), 5).filter(_ != myUnit)
res0: List[AnyVal] = List(1, 2, 3, 4, 5)**


#9

Collect seems to work in a simple way

List(1,2,3,() , () , 4, (), 5) collect {
     case a: Int => a
 }
res2: List[Int] = List(1, 2, 3, 4, 5)

#10

Interesting, thanks.