1

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 ?

4 Answers 4

3

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.

Sign up to request clarification or add additional context in comments.

5 Comments

can you explain what is happening in this function : '(i => (i._1, i._2.size))' ?
You could also use mapValues.
@Dan what are mapValues, is it an api ?
mapValues is just another method on the Map collection. It is similar to map except that it only provides access to the values in the collection (not the keys), so in the second example above, the equivalent would be grouped.mapValues(_.size).
It's amusing to note that I don't think I've ever seen anyone call .groupBy without following it with a .mapValues or a .map. Perhaps it's an unnecessarily general API?
1

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

Comments

0

Using only immutable map:

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.