blue-sky
blue-sky

Reputation: 53916

Converting mutable hashmap to immutable hashmap

I'm counting the number of urls within a list. To accomplish this I'm adding to a map where the key is the url and the value is the current counter. Each time I encounter the same key, I increment the counter. Here is the code :

    var m = new HashMap[String, Int]
    for(l <- MyList){
      val url = l.getUrl()
          var currentCount : Option[Int] = m.get(url)
          currentCount match {
                case Some(value) =>
                    var currentCount = value + 1
                    m = m ++ Map(url -> currentCount)
                case None =>
                    m = m ++ Map(url -> 1)
          }       
    }

I started with an immutable map but found I needed to reassign the map each time in order to maintain the counter values with the associated keys. Is there a solution to use an immutable map accomplish same task as above ?

Upvotes: 1

Views: 628

Answers (4)

rahul kunal
rahul kunal

Reputation: 36

create another map which would be mutable. and append it with the other map to get the new mutable map. e.g

val updatedMap = new HashMap[String, List[Employee]]
val merged = list.groupBy(_._1).map { case (k, v) => k -> v.map(_._2) }

val newMap = updatedMap ++ merged

Upvotes: 0

trenobus
trenobus

Reputation: 236

Using only immutable map:

    MyList.foldLeft(Map() : Map[String, Int]) { (map, elem) =>
      val key = elem.getUrl
      map + (key -> (map.getOrElse(key, 0) + 1))
    }

Upvotes: 0

Andreas Neumann
Andreas Neumann

Reputation: 10904

The mutable Map approach you have chosen ist quite suitable to the given tasks and should surpass most immutable implementations in used space and time. You should stick to it.

It would be good style to keep the mutability local:

def calculateMap(myList : List[ URL? ]) : immutable.Map[String,Int] = {
  var m = new scala.collection.mutable.HashMap[String, Int]
  for{
     l <- myList
     url = l.getUrl()
    }{
      val currentCount = m.get(url) getOrElse 0
      m += (url -> currentCount + 1)
  }
  Map() ++ m // this transforms m in an immutable map
}

Alternatively if you want to improve speed and the getUrl() method would block you can try to calculate the results in parallel and convert them to a map like this:

def calculateMapPar(myList : IndexedSeq[ URL? ]) : Map[String,Int] =
   myList.par.map(url => url.getUrl).groupBy(x => x).mapValues(_.size).seq

Upvotes: 1

jcern
jcern

Reputation: 7848

You could do something like:

MyList.groupBy(_.getUrl).map(i => (i._1, i._2.size))

That should give you am immutable Map, grouped by getUrl which contains the number of times getUrl was found.

Or, with type signatures for clarity:

val grouped Map[String, List[MyList]] = MyList.groupBy(_.getUrl)
grouped.map( i => (i._1, i._2.size)

What is happening is that the groupBy will group the list into a map whose key is getUrl and whose value is a List[MyList] where each item's getUrl is equal to the key.

The next line will just transform the Map[String, List[MyList]] into a Map[String, Int] by returning the key and the size of the list. The structure of a map is generally the same as a (key, value) tuple - so in the map, you can access the key and values accordingly.

Upvotes: 3

Related Questions