Scala - can a lambda parameter match a tuple?

Scala

Scala Problem Overview


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

Scala Solutions


Solution 1 - Scala

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.

Solution 2 - Scala

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)

Solution 3 - Scala

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

Solution 4 - Scala

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 )

Solution 5 - Scala

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
))

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondvmllsView Question on Stackoverflow
Solution 1 - ScalaKipton BarrosView Answer on Stackoverflow
Solution 2 - ScalaAmir RaminfarView Answer on Stackoverflow
Solution 3 - ScalaRex KerrView Answer on Stackoverflow
Solution 4 - ScalacomonadView Answer on Stackoverflow
Solution 5 - ScalaakauppiView Answer on Stackoverflow