Найти первый элемент, удовлетворяющий условию X в Seq
Как правило, как найти первый элемент, удовлетворяющий определенному условию в Seq
?
Например, у меня есть список возможных форматов даты, и я хочу найти проанализированный результат первого формата, который может проанализировать мою строку даты.
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
Неплохо. Но это немного уродливо. 2. он сделал некоторую ненужную работу (попробовал форматы "MM yyyy"
и "MM, yyyy"
). Возможно, есть более элегантный и идиоматический путь? (используя Iterator
?)
Ответы
Ответ 1
Если вы уверены, что по крайней мере один формат будет успешным:
formats.view.map{format => Try(format.parse(str)).toOption}.filter(_.isDefined).head
Если вы хотите быть немного безопаснее:
formats.view.map{format => Try(format.parse(str)).toOption}.find(_.isDefined)
Try
был введен в Scala 2.10.
A view
- это тип коллекции, который вычисляет значения лениво. Он будет применять код внутри Try
только к такому количеству элементов в коллекции, который необходим, чтобы найти первый, который определен. Если первый format
применяется к строке, то он не будет пытаться применить остальные форматы к строке.
Ответ 2
Вы должны использовать метод find
для последовательностей. Обычно вам следует использовать встроенные методы, поскольку они могут быть оптимизированы для определенной последовательности.
Console println List(1,2,3,4,5).find( _ == 5)
res: Some(5)
То есть, чтобы вернуть первый SimpleDateFormat, который соответствует:
val str = "1903 January"
val formats = List("MMM yyyy", "yyyy MMM", "MM yyyy", "MM, yyyy")
.map(new SimpleDateFormat(_))
formats.find { sdf =>
sdf.parse(str, new ParsePosition(0)) != null
}
res: Some([email protected])
Чтобы вернуть обработанную первую дату:
val str = "1903 January"
val formats = List("MMM yyyy", "yyyy MMM", "MM yyyy", "MM, yyyy").map(new SimpleDateFormat(_))
val result = formats.collectFirst {
case sdf if sdf.parse(str, new ParsePosition(0)) != null => sdf.parse(str)
}
или используйте ленивую коллекцию:
val str = "1903 January"
val formats = List("MMM yyyy", "yyyy MMM", "MM yyyy", "MM, yyyy").map(new SimpleDateFormat(_))
formats.toStream.flatMap { sdf =>
Option(sdf.parse(str, new ParsePosition(0)))
}.headOption
res: Some(Thu Jan 01 00:00:00 EET 1903)
Ответ 3
Это предотвращает ненужные оценки.
formats.collectFirst{ case format if Try(format.parse(str)).isSuccess => format.parse(str) }
Число оценок метода parse
- количество попыток + 1.
Ответ 4
scala> def parseOpt(fmt: SimpleDateFormat)(str: String): Option[Date] =
| Option(fmt.parse(str, new ParsePosition(0)))
tryParse: (str: String, fmt: java.text.SimpleDateFormat)Option[java.util.Date]
scala> formats.view.flatMap(parseOpt(fmt)).headOption
res0: Option[java.util.Date] = Some(Thu Jan 01 00:00:00 GMT 1903)
Кстати, поскольку SimpleDateFormat
не является потокобезопасным, это означает, что приведенный выше код не является потокобезопасным!
Ответ 5
Такая же версия с Scala Extractor и lazyness:
case class ParseSpec(dateString: String, formatter:DateTimeFormatter)
object Parsed {
def unapply(parsableDate: ParseSpec): Option[LocalDate] = Try(
LocalDate.parse(parsableDate.dateString, parsableDate.formatter)
).toOption
}
private def parseDate(dateString: String): Option[LocalDate] = {
formats.view.
map(ParseSpec(dateString, _)).
collectFirst { case Parsed(date: LocalDate) => date }
}