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.
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))}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With