3

I can write the following for Vectors:

def add[K,V](map: Map[K,Vector[V]], key: K, values: Vector[V]): Map[K,Vector[V]] = { map + (key -> (map.getOrElse(key, Vector.empty) ++ values)) } 

Usage:

scala> add(Map(1 -> Vector(1,2,3)), 1, Vector(4,5,6)) res1: Map[Int,Vector[Int]] = Map(1 -> Vector(1, 2, 3, 4, 5, 6)) 

The code for Sets is almost identical:

def add[K,V](map: Map[K,Set[V]], key: K, values: Set[V]): Map[K,Set[V]] = { map + (key -> (map.getOrElse(key, Set.empty) ++ values)) } 

How to make a single function that works for all Iterables? I tried to write it like this:

def add[K,V](map: Map[K,Iterable[V]], key: K, values: Iterable[V]): Map[K,Iterable[V]] = { map + (key -> (map.getOrElse(key, Iterable.empty) ++ values)) } 

But in this case I lost type info:

scala> add(Map(1 -> Set(1,2,3)), 1, Set(4,5,6)) res4: Map[Int,Iterable[Int]] = Map(1 -> Set(5, 1, 6, 2, 3, 4)) 

I tried the following:

def add[K,V,I[_] <: Iterable[_]](map: Map[K,I[V]], key: K, values: I[V]): Map[K,I[V]] = { map + (key -> (map.get(key).map(_ ++ values).getOrElse(values))) } 

But it didn't compile:

Cannot construct a collection of type That with elements of type Any based on a collection of type Repr. [error] map + (key -> (map.get(key).map(_ ++ values).getOrElse(values)))

2 Answers 2

5

You need to use CanBuildFrom to channel the type info about the used collection through the ++ call. See the overloads of ++ on TraversableLike for this.

The following works (not caring about details, like undefined keys).

def add[K,V,It <: TraversableLike[V,It]] (map: Map[K,It],k: K, vs: Traversable[V]) (implicit bf: CanBuildFrom[It,V,It]) :Map[K,It] = map + (k -> (map(k) ++ vs)) 
Sign up to request clarification or add additional context in comments.

3 Comments

Is there a way to use the function without fully specified type signature? I can't imagine writing those types all the time.
@tokarev I had a second look and removed an unescessary type parameter. It should work now.
Scala awesomeness in action. I want to print this code on a t-shirt
2

Ugly instanceof but works :)

def add[K,V,I[V] <: Iterable[V]](map: Map[K,I[V]], key: K, values: I[V]): Map[K,I[V]] = { val m: Map[K, Iterable[Any]] = map + (key -> (map.get(key).map(_ ++ values.asInstanceOf[Iterable[V]]).getOrElse(values))) m.asInstanceOf[Map[K, I[V]]] } 

Results from scala console:

scala> def add[K,V,I[V] <: Iterable[V]](map: Map[K,I[V]], key: K, values: I[V]): Map[K,I[V]] = { | val m: Map[K, Iterable[Any]] = map + (key -> (map.get(key).map(_ ++ values.asInstanceOf[Iterable[V]]).getOrElse(values))) | m.asInstanceOf[Map[K, I[V]]] | } warning: there were 1 feature warning(s); re-run with -feature for details add: [K, V, I[V] <: Iterable[V]](map: Map[K,I[V]], key: K, values: I[V])Map[K,I[V]] scala> add(Map(1 -> Vector(1,2,3)), 1, Vector(4,5,6)) res0: Map[Int,scala.collection.immutable.Vector[Int]] = Map(1 -> Vector(1, 2, 3, 4, 5, 6)) scala> add(Map(1 -> Set(1,2,3)), 1, Set(4,5,6)) res1: Map[Int,scala.collection.immutable.Set[Int]] = Map(1 -> Set(5, 1, 6, 2, 3, 4)) 

1 Comment

asInstanceOf is a code smell. +1 for a working solution though

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.