Scala - can a lambda parameter match a tuple?

So say i have some list like

val l = List((1, "blue"), (5, "red"), (2, "green"))

And then i want to filter one of them out, i can do something like

val m = l.filter(item => {
    val (n, s) = item          // "unpack" the tuple here
    n != 2
}

Is there any way i can "unpack" the tuple as the parameter to the lambda directly, instead of having this intermediate item variable?

Something like the following would be ideal, but eclipse tells me wrong number of parameters; expected=1

val m = l.filter( (n, s) => n != 2 )

Any help would be appreciated - using 2.9.0.1


This is about the closest you can get:

 val m = l.filter { case (n, s) => n != 2 }

It's basically pattern matching syntax inside an anonymous PartialFunction. There are also the tupled methods in Function object and traits, but they are just a wrapper around this pattern matching expression.


Hmm although Kipton has a good answer. You can actually make this shorter by doing.

val l = List((1, "blue"), (5, "red"), (2, "green"))
val m = l.filter(_._1 != 2)

There are a bunch of options:

for (x <- l; (n,s) = x if (n != 2)) yield x
l.collect{ case x @ (n,s) if (n != 2) => x }
l.filter{ case (n,s) => n != 2 }
l.unzip.zipped.map((n,s) => n != 2).zip   // Complains that zip is deprecated

val m = l.filter( (n, s) => n != 2 )

... is a type mismatch because that lambda defines a

  • Function2[String,Int,Boolean] with two parameters instead of
  • Function1[(String,Int),Boolean] with one Tuple2[String,Int] as its parameter.

You can convert between them like this:

val m = l.filter( ((n, s) => n != 2).tupled )

I've pondered the same, and came to your question today.

I'm not very fond of the partial function approaches (anything having case) since they imply that there could be more entry points for the logic flow. At least to me, they tend to blur the intention of the code. On the other hand, I really do want to go straight to the tuple fields, like you.

Here's a solution I drafted today. It seems to work, but I haven't tried it in production, yet.

object unTuple {
  def apply[A, B, X](f: (A, B) => X): (Tuple2[A, B] => X) = {
    (t: Tuple2[A, B]) => f(t._1, t._2)
  }
  def apply[A, B, C, X](f: (A, B, C) => X): (Tuple3[A, B, C] => X) = {
    (t: Tuple3[A, B, C]) => f(t._1, t._2, t._3)
  }
  //...
}

val list = List( ("a",1), ("b",2) )
val list2 = List( ("a",1,true), ("b",2,false) )

list foreach unTuple( (k: String, v: Int) =>
  println(k, v)
)

list2 foreach unTuple( (k: String, v: Int, b: Boolean) =>
  println(k, v, b)
)

Output:

(a,1)
(b,2)
(a,1,true)
(b,2,false)

Maybe this turns out to be useful. The unTuple object should naturally be put aside in some tool namespace.

Addendum:

Applied to your case:

val m = l.filter( unTuple( (n:Int,color:String) =>
    n != 2
))