给定List[Foo]
Foo
定义为:
case class Foo(bar: Option[String])
我最好的方法是按字母顺序找到第一个Foo
的{{1}}?
例如,如果我有:
bar
我想返回val l = List(Foo(None), Foo(Some("xyz")), Foo(Some("abc")))
答案 0 :(得分:2)
您可以使用sortBy
,然后使用collectFirst
模式匹配不包含None
的第一个元素:
l.sortBy(_.bar).collectFirst { case x @ Foo(Some(_)) => x }
请注意,这将返回Option[Foo]
,如果列表中的所有元素都包含None
,则上述代码将返回None
。否则Some(first matching element)
。
如果您非常确信该列表将始终包含非空Option
,请在结尾处使用get
。
答案 1 :(得分:2)
list.filter(_.bar.isDefined) match {
case Nil => Foo(None)
case l => l.minBy(_.bar)
}
或者,如果总会有至少一个非空选项,那么只需list.filter(_.bar.isDefined).minBy(_.bar)
这比(排序)好于排序,因为它是线性的,并且不需要分配数据结构。
另一种可能性,即甚至(一点点)更有效(但稍微更多地参与)
list.reduceOption {
case (Foo(None), x) => x
case (x, Foo(None)) => x
case (Foo(Some(x)), Foo(Some(y))) => if ( x < y ) Foo(Some(x)) else Foo(Some(y))
}
答案 2 :(得分:1)
这是我到目前为止看到的最实用的方法。它也不需要对任何东西进行排序:
case class Foo(bar: Option[String])
val l = List(Foo(None), Foo(Some("xyz")), Foo(Some("abc")))
val foo = l.foldLeft(Foo(None)) {
(acc, elem) => (for {
elemB <- elem.bar
accB <- acc.bar
smaller = if( elemB < accB) elem else acc
} yield smaller)
.getOrElse(elem)
}
System.out.println(foo.bar)
答案 3 :(得分:0)
您也可以使用foldLeft。它将更快地工作,因为不需要对列表进行排序:
import Ordering.Implicits._
val res = l.foldLeft(Foo(None))((res, foo) =>
if (res.bar.isEmpty) foo
else if (foo.bar.isEmpty) res
else if (res.bar < foo.bar) res
else foo)
println(res) // Foo(Some(abc))
答案 4 :(得分:0)
您可以在排序前过滤列表。
l.filter {_ match {
case Foo(None) => false
case _ => true
}
}.minBy(_.bar)