在 Scala 中转换地图

Transforming a map in Scala

我想在 Scala 中转换 String -> List[String] 类型的映射,以便每个 (k, (List v1, v2, ... vn)) 键值对都指向 {(k:v1, k), (k:v2, k), ... (k:vn, k)}.

比如我要改造

scala.collection.immutable.Map[String,List[String]] = Map(A -> List(a1, a2), B -> List(b1))

scala.collection.immutable.Map[String,List[String]] = Map(A:a1 -> A, A:a2 -> A, B:b1 -> B)

我能够使用[这个答案][1]部分实现我的目标:

scala> val schema = Map("A" -> List("a1", "a2"),  "B" -> List("b1"))
schema: scala.collection.immutable.Map[String,List[String]] = Map(A -> List(a1, a2), B -> List(b1))

scala>  schema flatten {case(k, vs) => vs.map((_, k))}
res1: scala.collection.immutable.Iterable[(String, String)] = List((a1,A), (a2,A), (b1,B))

当我尝试为每个值添加原始键和冒号时,出现错误:

scala> schema flatten {case(k, vs) => vs.map((k.concat(":").concat(_), k))}
<console>:13: error: type mismatch;
 found   : (String => String, String)
 required: String => ?
       schema flatten {case(k, vs) => vs.map((k.concat(":").concat(_), k))}
  1. 没有_(k.concat(":").concat(k), k)里面也没有变量,所以不是函数字面量,但是map的参数一定是函数。
  2. 无论如何您都不想将 kk 连接起来

试试这个:

schema.flatMap { case (k, vs) => vs.map(v => (k + ":" + v, k)) }

或更简洁:

for ((k, vs) <- schema; v <- vs) yield (k + ":" + v, k)

编辑

对于每个 k,表达式 k.concat(":").concat(_) 是一个接受字符串 s 并计算 k + ":" + s.

的函数

因此,(k.concat(":").concat(_), k)

的元组
  1. 函数 x => k.concat(":").concat(x)
  2. 价值k

所以它的类型是 (String => String, String)。这与 String => (String, String) 不同,这仍然不是您想要的。

错误消息告诉您 .map 采用 String => ? 形式的函数,但您向它传递的是 (String, String).

的元组

(k.concat(":").concat(k), k) 不是一个函数——而且我认为你并不是想在 concat.

中放置两次 k

要创建地图,您需要一个带有签名的函数 String => (String, String)

  • vs.map((k.concat(":").concat(k),k)) 错误地将 (String, String) 传递给 map
  • vs.map((k.concat(":").concat(_),k)) 错误地将 (String => String, String) 传递给映射。
  • vs.map(v => (k.concat(":").concat(v), k)) 正确匹配 String => (String, String).

然后我们可以得到:

schema flatten {case(k, vs) => vs.map(v => (k.concat(":").concat(v), k)) }
 //  List((A:a1,A), (A:a2,A), (B:b1,B)) 

您可以看到这段代码的实际效果 here

但是,正如 Andrey 的精彩评论所指出的那样,将 flatten 与隐式函数一起使用并不是好的做法。此外,您需要 Map 而不是 List。由于这两个原因,我认为你应该使用 flatMap 而不是 flatten:

schema flatMap {case(k, vs) => vs.map(v => (k.concat(":").concat(v), k)) };
// Map(A:a1 -> A, A:a2 -> A, B:b1 -> B)

如果您想尝试一下,请参阅 here