Find the first element that satisfies condition X

2020-01-30 07:33发布

Generally, how to find the first element satisfying certain condition in a Seq?

For example, I have a list of possible date format, and I want to find the parsed result of first one format can parse my date string.

val str = "1903 January"
val formats = List("MMM yyyy", "yyyy MMM", "MM yyyy", "MM, yyyy")
  .map(new SimpleDateFormat(_))
formats.flatMap(f => {try {
  Some(f.parse(str))
}catch {
  case e: Throwable => None
}}).head

Not bad. But 1. it's a little ugly. 2. it did some unnecessary work(tried "MM yyyy" and "MM, yyyy" formats). Perhaps there is more elegant and idiomatic way? (using Iterator?)

7条回答
劳资没心,怎么记你
2楼-- · 2020-01-30 08:15

Just use find method as it returns an Option of the first element matching predicate if any :

formats.find(str => Try(format.parse(str)).isSuccess)

Moreover, execution stops at the first match, so that you don't try parsing every element of your set before picking the first one. Here is an example :

def isSuccess(t: Int) = {
  println(s"Testing $t")
  Math.floorMod(t, 3) == 0
}
isSuccess: isSuccess[](val t: Int) => Boolean

List(10, 20, 30, 40, 50, 60, 70, 80, 90).filter(isSuccess).headOption
Testing 10
Testing 20
Testing 30
Testing 40
Testing 50
Testing 60
Testing 70
Testing 80
Testing 90
res1: Option[Int] = Some(30)

Stream(10, 20, 30, 40, 50, 60, 70, 80, 90).filter(isSuccess).headOption
Testing 10
Testing 20
Testing 30
res2: Option[Int] = Some(30)

List(10, 20, 30, 40, 50, 60, 70, 80, 90).find(isSuccess)
Testing 10
Testing 20
Testing 30
res0: Option[Int] = Some(30)

Note that for Stream it doesn't really matter. Also if you are using IntelliJ for example it will suggest you :

Replace filter and headOption with find.
Before:

seq.filter(p).headOption  

After:

seq.find(p)
查看更多
登录 后发表回答