Scala FlatMap提供了错误的结果
给定一个文档列表,我希望获得至少共享一个令牌的对。 为了做到这一点,我编写了下面的代码,它通过一个反向索引来实现Scala FlatMap提供了错误的结果,scala,flatten,flatmap,Scala,Flatten,Flatmap,给定一个文档列表,我希望获得至少共享一个令牌的对。 为了做到这一点,我编写了下面的代码,它通过一个反向索引来实现 object TestFlatMap { case class Document(id : Int, tokens : List[String]) def main(args: Array[String]): Unit = { val documents = List( Document(1, List("A", "B", "C", "D")), D
object TestFlatMap {
case class Document(id : Int, tokens : List[String])
def main(args: Array[String]): Unit = {
val documents = List(
Document(1, List("A", "B", "C", "D")),
Document(2, List("A", "B", "E", "F", "G")),
Document(3, List("E", "G", "H")),
Document(4, List("A", "L", "M", "N"))
)
val expectedTokensIds = List(("A",1), ("A",2), ("A",4), ("B",1), ("B",2), ("C",1), ("D",1), ("E",2), ("E",3), ("F",2), ("G",2), ("G",3), ("H",3), ("L",4), ("M",4), ("N",4)) //Expected tokens - id tuples
val expectedCouples = Set((1, 2), (1, 4), (2, 3), (2, 4)) //Expected resulting pairs
/**
* For each token returns the id of the documents that contains it
* */
val tokensIds = documents.flatMap{ document =>
document.tokens.map{ token =>
(token, document.id)
}
}
//Check if the tuples are right
assert(tokensIds.length == expectedTokensIds.length && tokensIds.intersect(expectedTokensIds).length == expectedTokensIds.length, "Error: tokens-ids not matches")
//Group the documents by the token
val docIdsByToken = tokensIds.groupBy(_._1).filter(_._2.size > 1)
/**
* For each group of documents generate the pairs
* */
val couples = docIdsByToken.map{ case (token, docs) =>
docs.combinations(2).map{ c =>
val d1 = c.head._2
val d2 = c.last._2
if(d1 < d2){
(d1, d2)
}
else{
(d2, d1)
}
}
}.flatten.toSet
/**
* Same operation, but with flatMap
* For each group of documents generate the pairs
* */
val couples1 = docIdsByToken.flatMap{ case (token, docs) =>
docs.combinations(2).map{ c =>
val d1 = c.head._2
val d2 = c.last._2
if(d1 < d2){
(d1, d2)
}
else{
(d2, d1)
}
}
}.toSet
//The results obtained with flatten pass the test
assert(couples.size == expectedCouples.size && couples.intersect(expectedCouples).size == expectedCouples.size, "Error: couples not matches")
//The results obtained with flatMap do not pass the test: they are wrong
assert(couples1.size == expectedCouples.size && couples1.intersect(expectedCouples).size == expectedCouples.size, "Error: couples1 not matches")
}
对象测试平面图{
案例类文档(id:Int,tokens:List[String])
def main(参数:数组[字符串]):单位={
val文档=列表(
文件(1,清单(“A”、“B”、“C”、“D”),
文件(2,清单(“A”、“B”、“E”、“F”、“G”),
文件(3,清单(“E”、“G”、“H”),
文件(4,列表(“A”、“L”、“M”、“N”))
)
val expectedTokensIds=List((“A”,1),(“A”,2),(“A”,4),(“B”,1),(“B”,2),(“C”,1),(“D”,1),(“E”,2),(“E”,3),(“F”,2),(“G”,2),(“G”,3),(“H”,3),(“L”,4),(“M”,4),(“N”,4))//预期的标记-id元组
val expectedCouples=Set((1,2)、(1,4)、(2,3)、(2,4))//预期的结果对
/**
*For each标记返回包含它的文档的id
* */
val tokensIds=documents.flatMap{document=>
document.tokens.map{token=>
(令牌,document.id)
}
}
//检查元组是否正确
断言(tokensIds.length==expectedTokensIds.length&&tokensIds.intersect(expectedTokensIds.length==expectedTokensIds.length,“错误:令牌ID不匹配”)
//按令牌对文档进行分组
val docIdsByToken=tokensIds.groupBy(u._1).filter(u._2.size>1)
/**
*为每组文档生成对
* */
val couples=docIdsByToken.map{case(token,docs)=>
docs.combinations(2).map{c=>
val d1=中心压头。\u 2
val d2=最后一次
如果(d1
docs.combinations(2).map{c=>
val d1=中心压头。\u 2
val d2=最后一次
如果(d1
问题是应该生成最终结果的flatMap不能正常工作,它只返回两对:(2,3)和(1,2)。
我不明白为什么它不起作用,而且IntelliJ建议我使用flatMap,而不是使用map然后展平
有人能告诉我问题出在哪里?因为我搞不清楚,我过去也有过这个问题
谢谢
Luca这是一个很好的例子,证明了如果在
map
/flatMap
/flattle
期间在不同类型的集合之间切换,那么所有的尼斯单子定律都不一定成立
您必须将
映射
转换为列表
,以便在构建另一个映射
作为中间结果时不会重复覆盖键,因为映射
将覆盖键,而不是收集所有对:
val couples1 = docIdsByToken.toList.flatMap{ case (token, docs) =>
docs.combinations(2).map{ c =>
val d1 = c.head._2
val d2 = c.last._2
if(d1 < d2){
(d1, d2)
}
else{
(d2, d1)
}
}
}.toSet
它将生成Set((2,1)、(2,3))
,而不是Set((2,3))
,因为
在flatMap
之后和toSet
之前,中间结果是一个新的Map
,该Map只能为key=2
保存一个值
与第一个版本不同的是,在
map
之后,您将获得类似于Iterable[List[(Int,Int)]]
的东西,它不是map
,因此不能丢失/覆盖任何键。令牌
从未被引用。这更有意义:val-couples1=docIdsByToken.values.flatMap{docs=>
..@jwvh我复制了一段经过编译、测试的代码(至少从表面上看,是通过目视检查输出的),并通过了OP提供的所有断言。该标记不应该被引用,它只充当数字之间的粘合剂。一旦找到数字对,字符串标记可以被丢弃。但它应该被和下划线替换。我保留它以尽可能多地保留原代码。这是最糟糕的情况之一Scala当前收藏库的“功能”。由于映射Set
和Map
,我有很多错误。你可以看看我曾经问过的关于这个问题的问题。@ziggystar问得好,但由于某些原因,我对解决方法没有印象。老实说,希望有更系统的东西:]
val m = Map("A" -> (2, 1), "B" -> (2, 3))
val s = m.flatMap{ case (k, v) => List(v) }.toSet
println(s)