在scala集合中,有一个collect方法,它是map和filter的组合。
还有另一种方法是flatMap和filter的组合吗?
这是我想要做的事情
val myList: List[Int] = ....
val x = myList.flatMap { id =>
val r : Option[List[Int]] = obj.foo(id)
r
}
现在,scala编译器告诉我x的类型是List[List[Int]]
但我想要List[Int]
如果我将代码更改为
val myList: List[Int] = ....
val x = myList.flatMap { id =>
val r : Option[List[Int]] = obj.foo(id).get
r
}
然后我得到了我想要的东西。但我不想做。所以我想要一个干净简洁的方法来做一个flatMap,同时过滤掉None的项目。
我也可以
val myList: List[Int] = ....
val result = myList.flatMap { id =>
val r : Option[List[Int]] = obj.foo(id).getOrElse(List[Int]())
r
}
但这仍然非常冗长。
答案 0 :(得分:3)
好吧,如果您在使用flatMap
时尝试过滤,可以轻松地执行类似
List(1, 2, 3).flatMap {
case n if n > 1 => List.fill(n)(n.toString)
case _ => Nil
}
// result: List("2", "2", "3", "3", "3")
在您的具体案例中:
myList.flatMap { id => obj.foo(id) match {
case Some(list) => list
case None => Nil
}}
甚至更短
myList.flatMap(obj.foo(_).getOrElse(Nil))
答案 1 :(得分:1)
我认为 for 表达式可以解决这个问题:
val myList: List[Int] = ....
val x = for {
id <- myList
r <- obj.foo(id)
} yield r
实际上,表达式已翻译为flatMap
,map
和filter
(请参阅http://docs.scala-lang.org/tutorials/FAQ/yield.html)
答案 2 :(得分:0)
如果要将.flatten
添加到最后,您的初始代码将完美无缺。
以下是我的写作方式:
val x1: List[Int] = myList.flatMap(obj.foo).flatten
或者如果您出于某种原因确实需要collect
,那么如下:
val x2: List[Int] = myList.map(obj.foo).collect{
case Some(data) => data
}.flatten
完整的来源是:
object Test3 extends App {
object obj {
def foo(i: Int) = (0 to i).toList match {
case Nil => None
case nonEmpty => Some(nonEmpty)
}
}
val myList: List[Int] = (0 to 3).toList
val x = myList.flatMap { id =>
val r : Option[List[Int]] = obj.foo(id)
r
}.flatten
val x1: List[Int] = myList.flatMap(obj.foo).flatten
val x2: List[Int] = myList.map(obj.foo).collect{
case Some(data) => data
}.flatten
}
答案 3 :(得分:0)
filter
/ flatMap
), None
实际上可以通过List.empty
表达,因此flatMap
比{filter
更通用1}}(至少List
和Option
s)。您的问题是需要应用两次:
myList.flatMap(obj.foo).flatMap(x => x)
或只是:
myList.flatMap(obj.foo).flatten
只是一个简单的说明:如果您有List[List[T]]
类型,或更常见的M[M[T]]
,并且您希望获得M[T]
,则通常会涉及flatMap
/ {{1} }
示例:
flatten