How can I convert immutable.Map to mutable.Map in Scala?
Asked Answered
P

6

109

How can I convert immutable.Map to mutable.Map in Scala so I can update the values in Map?

Patois answered 18/2, 2011 at 15:4 Comment(0)
K
138

The cleanest way would be to use the mutable.Map varargs factory. Unlike the ++ approach, this uses the CanBuildFrom mechanism, and so has the potential to be more efficient if library code was written to take advantage of this:

val m = collection.immutable.Map(1->"one",2->"Two")
val n = collection.mutable.Map(m.toSeq: _*) 

This works because a Map can also be viewed as a sequence of Pairs.

Kennykeno answered 19/2, 2011 at 12:3 Comment(6)
Can you explain, what syntax you use in the second line when passing the parameter? What does the colon do?Woodland
: _* is much like type ascription, telling the compiler exactly what type to asign to a given expression. You can think of it here as saying "take this sequence, and treat it as a number of vararg params."Kennykeno
There's something wrong with the collection libraries if this is the cleanest ;)Monger
@matt It could be made a bit shorter with aliased imports, but bear in mind that sacrificing immutability is very non-idiomatic for Scala, not exactly the sort of thing I'd enourage by making it look even easier... Out of curiosity, how else could you propose doing it more cleanly, if not via a copy?Kennykeno
That is my point, I can't, but a better collections library could make this possible, IMHO.Monger
@Kevin Wright it would be cleaner if the mutable map supported construction from immutable map types eg. val m = collection.immutable.Map(1->"one",2->"Two") val n = collection.mutable.Map(m)Papist
Z
47
val myImmutableMap = collection.immutable.Map(1->"one",2->"two")
val myMutableMap = collection.mutable.Map() ++ myImmutableMap
Zelig answered 18/2, 2011 at 15:11 Comment(4)
Do you know hat the asymptotic time complexity of this is? I know that Clojure can turn any of its persistent collections into a "transient" one (i.e. a mutable one with linearly-typed mutation functions) and back into a persistent one in O(1) steps. This looks to be O(n), although that depends of course on how clever the implementation of ++ is.Sanctitude
@Jörg - I'm pretty sure this one is O(n). In the limit as you change everything, it must be O(n), though you could try to defer the creation of the new copy to save time, or you double your access times by reading changesets instead of the original map. Which one performs best probably depends on your use case.Zelig
@Rustem - Maps are unordered. They'll appear in whichever order they feel like (with a hash map, it's typically the order of the hash key). In particular, immutable maps have special cases for really tiny maps which are different from mutable maps.Zelig
@Rustem Maps are not ordered.Consonantal
E
13

Starting Scala 2.13, via factory builders applied with .to(factory):

Map(1 -> "a", 2 -> "b").to(collection.mutable.Map)
// collection.mutable.Map[Int,String] = HashMap(1 -> "a", 2 -> "b")
Elimination answered 30/6, 2019 at 8:9 Comment(0)
U
4

How about using collection.breakOut?

import collection.{mutable, immutable, breakOut}
val myImmutableMap = immutable.Map(1->"one",2->"two")
val myMutableMap: mutable.Map[Int, String] = myImmutableMap.map(identity)(breakOut)
Upton answered 18/2, 2011 at 16:47 Comment(1)
It is cool, but basically does the same thing as mutable.Map#apply with a bit more boilerplate.Kennykeno
H
4

With scala 2.13, there are two alternatives: the to method of the source map instance, or the from method of the destination map's companion object.

scala> import scala.collection.mutable
import scala.collection.mutable

scala> val immutable = Map(1 -> 'a', 2 -> 'b');
val immutable: scala.collection.immutable.Map[Int,Char] = Map(1 -> a, 2 -> b)

scala> val mutableMap1 = mutable.Map.from(immutable)
val mutableMap1: scala.collection.mutable.Map[Int,Char] = HashMap(1 -> a, 2 -> b)

scala> val mutableMap2 = immutable.to(mutable.Map)
val mutableMap2: scala.collection.mutable.Map[Int,Char] = HashMap(1 -> a, 2 -> b)

As you can see, the mutable.Map implementation was decided by the library. If you want to choose a particular implementation, for example mutable.HashMap, replace all occurrences of mutable.Map with mutable.HashMap.

Humanitarianism answered 10/10, 2020 at 13:49 Comment(0)
B
2

There is a variant to create an empty mutable Map that has default values taken from the immutable Map. You may store a value and override the default at any time:

scala> import collection.immutable.{Map => IMap}
//import collection.immutable.{Map=>IMap}

scala> import collection.mutable.HashMap
//import collection.mutable.HashMap

scala> val iMap = IMap(1 -> "one", 2 -> "two")
//iMap: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,one), (2,two))

scala> val mMap = new HashMap[Int,String] {      
     | override def default(key: Int): String = iMap(key)
     | }
//mMap: scala.collection.mutable.HashMap[Int,String] = Map()

scala> mMap(1)
//res0: String = one

scala> mMap(2)
//res1: String = two

scala> mMap(3)
//java.util.NoSuchElementException: key not found: 3
//  at scala.collection.MapLike$class.default(MapLike.scala:223)
//  at scala.collection.immutable.Map$Map2.default(Map.scala:110)
//  at scala.collection.MapLike$class.apply(MapLike.scala:134)
//  at scala.collection.immutable.Map$Map2.apply(Map.scala:110)
//  at $anon$1.default(<console>:9)
//  at $anon$1.default(<console>:8)
//  at scala.collection.MapLike$class.apply(MapLike.scala:134)....

scala> mMap(2) = "three"

scala> mMap(2)          
//res4: String = three

Caveat (see the comment by Rex Kerr): You will not be able to remove the elements coming from the immutable map:

scala> mMap.remove(1)
//res5: Option[String] = None

scala> mMap(1)
//res6: String = one
Burrton answered 18/2, 2011 at 18:9 Comment(2)
This is useful in some cases, but note that you are unable to remove an element in your new map that was present in your default map; you can only cover and uncover the defaults.Zelig
Right, this solution is partial.Burrton

© 2022 - 2024 — McMap. All rights reserved.