47

I find myself writing code like the following:

val b = a map (entry =>
    entry match {
        case ((x,y), u) => ((y,x), u)
    }
)

I would like to write it differently, if only this worked:

val c = a map (((x,y) -> u) =>
    (y,x) -> u
)

Is there any way I can get something close to this?

Owen
  • 38,836
  • 14
  • 95
  • 125

4 Answers4

68

Believe it or not, this works:

val b = List(1, 2)
b map {
  case 1 => "one"
  case 2 => "two"
}

You can skip the p => p match in simple cases. So this should work:

val c = a map {
  case ((x,y) -> u) => (y,x) -> u
}
Cory Klein
  • 51,188
  • 43
  • 183
  • 243
sblundy
  • 60,628
  • 22
  • 121
  • 123
  • With these constructs, I would suggest always explicitly typing the collection you're mapping over. Otherwise, unintended widening of the static type (via type inference or refactoring) could lead to type errors that go undetected until runtime: https://issues.scala-lang.org/browse/SI-4670 – Aaron Novstrup Jun 23 '11 at 20:43
28

In your example, there are three subtly different semantics that you may be going for.

  1. Map over the collection, transforming each element that matches a pattern. Throw an exception if any element does not match. These semantics are achieved with

    val b = a map { case ((x, y), u) => ((y, x), u) }
    
  2. Map over the collection, transforming each element that matches a pattern. Silently discard elements that do not match:

    val b = a collect { case ((x, y), u) => ((y, x), u) }
    
  3. Map over the collection, safely destructuring and then transforming each element. These are the semantics that I would expect for an expression like

    val b = a map (((x, y), u) => ((y, x), u)))  
    

    Unfortunately, there is no concise syntax to achieve these semantics in Scala. Instead, you have to destructure yourself:

    val b = a map { p => ((p._1._2, p._1._1), p._2) }
    

    One might be tempted to use a value definition for destructuring:

    val b = a map { p => val ((x,y), u) = p; ((y, x), u) }
    

    However, this version is no more safe than the one that uses explicit pattern matching. For this reason, if you want the safe destructuring semantics, the most concise solution is to explicitly type your collection to prevent unintended widening and use explicit pattern matching:

    val a: List[((Int, Int), Int)] = // ...
    // ...
    val b = a map { case ((x, y), u) => ((y, x), u) }
    

    If a's definition appears far from its use (e.g. in a separate compilation unit), you can minimize the risk by ascribing its type in the map call:

    val b = (a: List[((Int, Int), Int)]) map { case ((x, y), u) => ((y, x), u) }
    
Aaron Novstrup
  • 20,967
  • 7
  • 70
  • 108
10

In your quoted example, the cleanest solution is:

val xs = List((1,2)->3,(4,5)->6,(7,8)->9)
xs map { case (a,b) => (a.swap, b) }
Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
4
val b = a map { case ((x,y), u) => ((y,x), u) }
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681