conetfun
conetfun

Reputation: 1615

How to merge two maps keeping greater of values for matching keys?

I have two mutable maps in Scala.

val oldMap = Map(10 -> 100, 20 -> 200, 30 -> 300)
val newMap = Map(10 -> 101, 20 -> 200, 30 -> 299, 40 -> 400)

I want to merge newMap into oldMap to get outputMap as below which have all keys from both maps but value is greater of matching key's value.

Map(20 -> 200, 40 -> 400, 10 -> 101, 30 -> 300)

I have tried below and it works but I wanted to know scala way of doing it.

import scala.collection.mutable.Map

object Test extends App {

  val oldMap = Map(10 -> 100, 20 -> 200, 30 -> 300)
  val newMap = Map(10 -> 101, 20 -> 200, 30 -> 299, 40 -> 400)
  val outputMap = mergeMap(oldMap, newMap)

  println(outputMap)

  def mergeMap(map1: Map[Int, Int], map2: Map[Int, Int]): Map[Int, Int] = {
    val map1Keys = map2.keys
    val itr = map1Keys.iterator
    while (itr.hasNext)
    {
      val id = itr.next
      if (! map1.contains(id)){ // key not present in map1, INSERT
        map1(id) = map2(id)
      }
      else { // key present in map1, UPDATE
        if (map2(id) > map1(id)){
          map1(id) = map2(id)
        }
      }

    }
    map1
  }

  def commonMapKeys[A, B](a: Map[A, B], b: Map[A, B]): scala.collection.Set[A] = a.keySet.intersect(b.keySet)

}

Upvotes: 1

Views: 598

Answers (2)

Guru Stron
Guru Stron

Reputation: 143383

You can turn maps into collection of tuples, merge them, group by key and select max value in the group:

val oldMap = Map(10 -> 100, 20 -> 200, 30 -> 300)
val newMap = Map(10 -> 101, 20 -> 200, 30 -> 299, 40 -> 400)
(oldMap.toSeq ++ newMap.toSeq)
  .groupBy(_._1)
  .mapValues(_.map(_._2).max)

Or using groupMapReduce available since Scala 2.13:

(oldMap.toSeq ++ newMap.toSeq)
    .groupMapReduce(_._1)(_._2)(math.max(_,_))

Upvotes: 5

zagyi
zagyi

Reputation: 17528

newMap.foldLeft(oldMap){ case (result, (k, newV)) =>
  val oldV = result.get(k)
  result + (k -> oldV.fold(newV)(newV max _))
}

You start from oldMap and iterate through newMap using .foldLeft. In each iteration you take a key and a value from newMap, and try to get the value mapped to the same key from the result map (which is the oldMap at the beginning). You add a new mapping from k to either the new value, or the max of the new and the old values, in case the old map also contained k.

It's pretty much the same thing that you have, just in a more functional way.

Upvotes: 3

Related Questions