scala:两个键类型相同且值类型为元素集合但类型不同的映射的并集

Yan*_*eve 1 collections scala idiomatic

我想创建两个映射的联合,这些映射的键类型相同,其值类型是元素的集合,但类型不同。

考虑以下人为的示例:

case class Child(name: String)
val peopleToChildren: Map[String, Seq[Child]] = 
  Map("max" -> Seq(Child("a"), Child("b")), 
    "yaneeve" -> Seq(Child("y"), Child("d")))

case class Pet(name: String)
val peopleToPets: Map[String, Seq[Pet]] = 
  Map("max" -> Seq(Pet("fifi")), 
    "jill" -> Seq(Pet("bobo"), Pet("jack"), Pet("Roger rabbit")))

val peopleToChildrenAndDogs: Map[String, (Seq[Child], Seq[Pet])] = {
  // people may have children
  // people may have pets
  // would like a map from people to a tuple with a potentially empty list of children and a
  //     potentially empty list of pets
  // ???
}
Run Code Online (Sandbox Code Playgroud)

简洁,惯用但仍清晰可辨的方法是什么?

我在标准的scala集合库中没有找到可以执行此操作的函数。

提出的解决方案可以仅基于标准库,也可以提出外部解决方案。

我将其发布在这里,因为我无法轻松地找到看似标准操作的在线解决方案。

jwv*_*wvh 5

这似乎起作用。

val peopleToChildrenAndDogs: Map[String, (Seq[Child], Seq[Pet])] = {
  (peopleToChildren.keySet ++ peopleToPets.keySet).map { k =>
    k -> (peopleToChildren.getOrElse(k, Seq())
         ,peopleToPets.getOrElse(k, Seq()))
  }.toMap
}
Run Code Online (Sandbox Code Playgroud)

获取所有密钥。对于每个键,getOrElse()在每个供稿器映射上执行一个。


Ole*_*cov 5

出于好奇,这里是如何使用 Scalaz 完成的:

import scalaz._, Scalaz._

case class Child(name: String)

val peopleToChildren = Map(
  "max"     -> List(Child("a"), Child("b")), 
  "yaneeve" -> List(Child("y"), Child("d"))
)

case class Pet(name: String)

val peopleToPets = Map(
  "max"  -> List(Pet("fifi")), 
  "jill" -> List(Pet("bobo"), Pet("jack"), Pet("Roger rabbit"))
)

val peopleToChildrenAndPets: Map[String, (List[Child], List[Pet])] = 
  peopleToChildren.strengthR(nil[Pet]) |+| peopleToPets.strengthL(nil[Child])
Run Code Online (Sandbox Code Playgroud)

解释:

  • nil[Pet] 只是一个别名 List.empty[Pet]
  • strengthR对于给定的Functor元组包含的值,因此其参数位于右侧。这里相当于peopleToChildren.mapValues(v => (v, nil[Pet]))
  • strengthL 是一样的,但元素会被添加到左边
  • |+|是给定 的附加运算符Semigroup。这里的一个是递归派生的:
    • for Map[K, V],如果给定的键存在于两个 Map 中,则它用于|+|组合 type 的值V。如果该值仅存在于其中之一中,它将按原样保留。这里,V = (List[Child], List[Pet])
    • 对于 tuples (A, B),它再次用于|+|组合As 和Bs 。在这里,A = List[Child]B = List[Pet]
    • 对于任何类型的列表(以及字符串、向量或流),它都会进行连接。这就是为什么我必须将 Map 值的类型更改为Lists - 对于泛型Seqs 未定义此操作

结果:

peopleToChildrenAndPets: Map[String, (List[Child], List[Pet])] = Map(
  "max" -> (List(Child("a"), Child("b")), List(Pet("fifi"))),
  "jill" -> (
    List(),
    List(Pet("bobo"), Pet("jack"), Pet("Roger rabbit"))
  ),
  "yaneeve" -> (List(Child("y"), Child("d")), List())
)
Run Code Online (Sandbox Code Playgroud)