Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between mapValues and transform in Map

In Scala Map (see API) what is the difference in semantics and performance between mapValues and transform ?

For any given map, for instance

val m = Map( "a" -> 2, "b" -> 3 ) 

both

m.mapValues(_ * 5) m.transform( (k,v) => v * 5 ) 

deliver the same result.

like image 669
elm Avatar asked Sep 03 '14 03:09

elm


2 Answers

Let's say we have a Map[A,B]. For clarification: I'm always referring to an immutable Map.

mapValues takes a function B => C, where C is the new type for the values.

transform takes a function (A, B) => C, where this C is also the type for the values.

So both will result in a Map[A,C].

However with the transform function you can influence the result of the new values by the value of their keys.

For example:

val m = Map( "a" -> 2, "b" -> 3 ) m.transform((key, value) => key + value) //Map[String, String](a -> a2, b -> b3) 

Doing this with mapValues will be quite hard.

The next difference is that transform is strict, whereas mapValues will give you only a view, which will not store the updated elements. It looks like this:

protected class MappedValues[C](f: B => C) extends AbstractMap[A, C] with DefaultMap[A, C] {   override def foreach[D](g: ((A, C)) => D): Unit = for ((k, v) <- self) g((k, f(v)))   def iterator = for ((k, v) <- self.iterator) yield (k, f(v))   override def size = self.size   override def contains(key: A) = self.contains(key)   def get(key: A) = self.get(key).map(f) } 

(taken from https://github.com/scala/scala/blob/v2.11.2/src/library/scala/collection/MapLike.scala#L244)

So performance-wise it depends what is more effective. If f is expensive and you only access a few elements of the resulting map, mapValues might be better, since f is only applied on demand. Otherwise I would stick to map or transform.

transform can also be expressed with map. Assume m: Map[A,B] and f: (A,B) => C, then

m.transform(f) is equivalent to m.map{case (a, b) => (a, f(a, b))}

like image 138
Kigyo Avatar answered Sep 28 '22 22:09

Kigyo


collection.Map doesn't provide transform: it has a different signature for mutable and immutable Maps.

$ scala Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_11). Type in expressions to have them evaluated. Type :help for more information.  scala> val im = Map('a -> 1, 'b -> 2, 'c -> 3) im: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 1, 'b -> 2, 'c -> 3)  scala> im.mapValues(_ * 7) eq im res0: Boolean = false  scala> im.transform { case (k,v) => v*7 } eq im res2: Boolean = false  scala> val mm = collection.mutable.Map('a -> 1, 'b -> 2, 'c -> 3) mm: scala.collection.mutable.Map[Symbol,Int] = Map('b -> 2, 'a -> 1, 'c -> 3)  scala> mm.mapValues(_ * 7) eq mm res3: Boolean = false  scala> mm.transform { case (k,v) => v*7 } eq mm res5: Boolean = true 

Mutable transform mutates in place:

scala> mm.transform { case (k,v) => v*7 } res6: mm.type = Map('b -> 98, 'a -> 49, 'c -> 147)  scala> mm.transform { case (k,v) => v*7 } res7: mm.type = Map('b -> 686, 'a -> 343, 'c -> 1029) 

So mutable transform doesn't change the type of the map:

scala> im mapValues (_ => "hi") res12: scala.collection.immutable.Map[Symbol,String] = Map('a -> hi, 'b -> hi, 'c -> hi)  scala> mm mapValues (_ => "hi") res13: scala.collection.Map[Symbol,String] = Map('b -> hi, 'a -> hi, 'c -> hi)  scala> mm.transform { case (k,v) => "hi" } <console>:9: error: type mismatch;  found   : String("hi")  required: Int               mm.transform { case (k,v) => "hi" }                                            ^  scala> im.transform { case (k,v) => "hi" } res15: scala.collection.immutable.Map[Symbol,String] = Map('a -> hi, 'b -> hi, 'c -> hi) 

...as can happen when constructing a new map.

like image 20
som-snytt Avatar answered Sep 28 '22 23:09

som-snytt