一般来说,如何在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
?)
答案 0 :(得分:21)
您应该对序列使用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(java.text.SimpleDateFormat@ef736ccd)
要返回正在处理的第一个日期:
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)
}
或使用 lazy collection :
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)
答案 1 :(得分:15)
如果您确信至少有一种格式会成功:
formats.view.map{format => Try(format.parse(str)).toOption}.filter(_.isDefined).head
如果你想要更安全一点:
formats.view.map{format => Try(format.parse(str)).toOption}.find(_.isDefined)
Scala 2.10中引入了 Try
。
view
是一种集合,可以懒惰地计算值。它将Try
中的代码仅应用于集合中的所需项目,以查找定义的第一个项目。如果第一个format
适用于字符串,则它不会尝试将其余格式应用于字符串。
答案 2 :(得分:9)
这可以防止不必要的评估。
formats.collectFirst{ case format if Try(format.parse(str)).isSuccess => format.parse(str) }
parse
方法的评估次数是尝试次数+ 1。
答案 3 :(得分:3)
与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 }
}
答案 4 :(得分:2)
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 :(得分:2)
只需将find方法用作it returns an Option of the first element matching predicate if any即可:
formats.find(str => Try(format.parse(str)).isSuccess)
此外,执行在第一个匹配项处停止,因此您在选择第一个匹配项之前不必尝试解析集合中的每个元素。这是一个示例:
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)
请注意,对于Stream来说,这并不重要。 同样,如果您使用的是IntelliJ,它也会建议您:
用find替换过滤器和headOption。
之前:
seq.filter(p).headOption
之后:
seq.find(p)
答案 6 :(得分:1)
我认为使用尾递归要好得多,到目前为止,这是迄今为止提供的最有效的解决方案:
implicit class ExtendedIterable[T](iterable: Iterable[T]) {
def findFirst(predicate: (T) => Boolean): Option[T] = {
@tailrec
def findFirstInternal(remainingItems: Iterable[T]): Option[T] = {
if (remainingItems.nonEmpty)
if (predicate(remainingItems.head))
Some(remainingItems.head)
else
findFirstInternal(remainingItems.tail)
else
None
}
findFirstInternal(iterable)
}
}
允许您在导入上述课程时,只需执行以下操作即可:
formats.findFirst(format => Try(format.parse(str)).isSuccess)
祝你好运!
答案 7 :(得分:0)
使用org.joda.time:
定义:
def getBaseLocalFromFormats[T <: BaseLocal](
value: String,
validPatterns: Seq[String],
parse: (String, String) => T) : Option[T] = {
validPatterns.view.map(p => Try{ parse(value, p) }).find(_.isSuccess).map(_.get)
}
用法:
getBaseLocalFromFormats(
"01/10/1980 16:08:22",
List("dd/MM/yyyy HH:mm:ss"),
(v,p) => DateTimeFormat.forPattern(p).parseLocalDateTime(v))
getBaseLocalFromFormats(
"01/10/1980",
List("dd/MM/yyyy", "dd-MM-yyyy", "yyyy-MM-dd"),
(v,p) => DateTimeFormat.forPattern(p).parseLocalDate(v))