Iterables的映射 - 在给定键下为迭代添加值的通用方法

时间:2013-08-23 10:59:38

标签: scala

我可以为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))
}

用法:

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))

集合的代码几乎相同:

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))
}

如何制作适用于所有Iterables的单一功能?我试着像这样写:

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))
}

但在这种情况下我丢失了类型信息:

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))

我尝试了以下内容:

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)))
}

但它没有编译:

  

无法根据Repr类型的集合构造类型为Any的元素类型的集合。   [error] map +(key - &gt;(map.get(key).map(_ ++ values).getOrElse(values)))

2 个答案:

答案 0 :(得分:5)

您需要使用CanBuildFrom通过++来调用有关所用集合的类型信息。请参阅++TraversableLike的重载。

以下作品(不关心细节,如未定义的键)。

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))

答案 1 :(得分:2)

丑陋的instanceof但是有效:)

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]]]
}

scala控制台的结果:

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))