我有以下代码:
val dummy = Map(1 -> Map(2 -> 3.0,
4 -> 5.0),
6 -> Map(7 -> 8.0))
val thisIsList = for (x <- dummy; y <- x._2.keys) yield s"(${x._1}, ${y})"
println(thisIsList) // List((1, 2), (1, 4), (6, 7))
val thisIsMap = for (x <- dummy; y <- x._2.keys) yield new Tuple2(x._1, y)
println(thisIsMap) // Map(1 -> 4, 6 -> 7) - this is not what I want
我希望第二条语句产生一个元组列表,但它返回一个Map。 我在scala: yield a sequence of tuples instead of map处找到了有关为什么返回Map的解释,但在这种情况下,我仍然在努力寻找一种优雅的方法来返回元组列表。
答案 0 :(得分:7)
这是因为编译器如何将for
理解语法转换为一系列方法调用。 map
理解的排列以flatMap
,withFilter
和for
为目标。这是非常强大且通用的,因为它允许语法与任意类型一起使用。除此之外,还有其他含义,例如隐式CanBuildFrom
,但实际上将Map
映射到Iterable[Tuple[A, B]]
会产生Map[A, B]
。 The signature is actually overloaded for Map
to provide this behavior
翻译大致像这样
val thisIsMap1 = dummy.flatMap { x =>
x._2.keys.map { y =>
(x._1, y)
}
}
查看此fiddle
为了获得所需的列表,我们可以编写
val thisIsMap = (for (x <- dummy; y <- x._2.keys) yield (x._1, y)).toList
但是,如果考虑一下我们对for
理解的了解,我们可以将其写得更加优雅
val thisIsMap = for (x <- dummy.toList; y <- x._2.keys) yield (x._1, y)
在上文中,我们通过推断对for
的{{1}}理解将产生List
来利用混淆了原始代码的行为。
但是 ,请注意,在理解后,将源转换为List
与将结果映射转换为List
之间的区别
如果在源(List
上调用toList
,则得到dummy
;而如果在结果上调用它,则得到List((1,2), (1,4), (6,7))
,这是显而易见的原因,因此仔细选择。
答案 1 :(得分:2)
尝试
dummy
.view
.mapValues(_.keys.toList)
.flatMap { case (key: Int, values: List[Int]) => values.map((key, _)) }
.toList
输出
res0: List[(Int, Int)] = List((1,2), (1,4), (6,7)
答案 2 :(得分:0)
在回答了所有问题之后,将在此处发布我自己的问题的TLDR摘要。
由 for 理解循环返回的数据结构的类型应该与 for 循环开始进行迭代的类型相同。 即如果开始在Map上进行迭代-希望Map成为最终结果。
val thisIsList = for (x <- dummy; y <- x._2.keys) yield s"(${x._1}, ${y})"
println(thisIsList) // List((1, 2), (1, 4), (6, 7))
在这个问题示例中, for 循环开始在Map上进行迭代,但返回一个List。发生这种情况是因为yield不会返回可转换为Map的类型。但是它可以转换为列表,因此Scala可以做到。
val thisIsMap = for (x <- dummy; y <- x._2.keys) yield new Tuple2(x._1, y)
println(thisIsMap) // Map(1 -> 4, 6 -> 7) - this is not what I want
尽管在此示例中,一切都按预期进行,但是由于生成的Map不能具有重复的键,因此元组(1,2)被元组(1,4)覆盖。即该地图仅包含2个元素。