Card Merge Enhancements

I am writing a function to combine two cards together. This is what I have so far:

def merge[K, V1, V2, V3](left: Map[K, V1], right: Map[K, V2]) (fn: (Option[V1], Option[V2]) => V3): Map[K, V3] = { val r = (left.keySet ++ right.keySet) map { key => (key -> fn(left.get(key), right.get(key))) } r.toMap } 

The function itself works. You use this function like this:

 val m1 = Map(1 -> "one", 3 -> "three", 5 -> "five") val m2 = Map(1 -> "I", 5 -> "V", 10 -> "X") merge(m1, m2) { (_, _) } // returns: // Map(1 -> (Some(one),Some(I)), // 3 -> (Some(three),None), // 5 -> (Some(five),Some(V)), // 10 -> (None,Some(X))) 

I have two questions:

  • I'm worried about the complexity of the execution computational complexity of .get and .toMap . Can anyone improve the implementation?
  • I need a default function to make a pair of values ​​( { (_, _) } ). I can't get the syntax to do it right.

Edit: Although I originally talked about performance, I meant computational complexity. I assume that this function executes in O (n • ln (n)) time. It looks like my function is executing roughly in O (n). Can this be done in O (ln (n))?

+4
source share
2 answers

For the default function symbol, use:

 (fn: (Option[V1], Option[V2]) => V3 = (x: Option[V1], y: Option[V2]) => Tuple2(x,y)) 

You will need to use the merge as follows: merge(m1,m2)()

I would say don't worry about performance until you take some actual data measurements.

Edit: about performance, providing an idea instead of building a map, you can quickly “build” through a search - provided that we are dealing with immutable maps. Thus, depending on the evidence and use cases, you may get better performance for certain operations, but it has a trade-off.

 class MergedView[K, V1, V2, V3]( left: Map[K, V1], right: Map[K, V2] )(fn: (Option[V1], Option[V2]) => V3 = (x: Option[V1], y: Option[V2]) => Tuple2(x,y) ) extends collection.DefaultMap[K, V3] { def get(key: K): Option[V3] = (left.get(key), right.get(key)) match { case (None, None) => None case t => Some(fn(t._1, t._2)) } lazy val tuples = (left.keys ++ right.keys).map(key => key -> get(key).get) def iterator: Iterator[(K, V3)] = tuples.iterator } val r1 = new MergedView(m1, m2)() // use parens here for second param list. 
+3
source

You should not worry about get - yes, it will create a wrapper object, but doing something else will be inconvenient, and you should not try if the profiler does not show that this is a problem.

As for toMap , yes, it can slow you down a lot. You can try using breakOut .

Regarding the complexity of get and toMap searching and adding is an efficient constant time for the immutable HashMap , which is Map by default. See Performance Characteristics of the Scala Collections .

+1
source

All Articles