2013-06-02 70 views
21

通常,如何在Seq中找到满足一定条件的第一个元素?查找满足条件X的第一个元素,在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 

不错。但是1.这有点难看。 2.它做了一些不必要的工作(尝试了"MM yyyy""MM, yyyy"格式)。也许有更优雅和习惯的方式? (使用Iterator?)

+0

使用'Seq' – Kakaji

回答

13

如果你有信心至少一个将格式化会成功:

formats.view.map{format => Try(format.parse(str)).toOption}.filter(_.isDefined).head 

如果你想成为一个更加安全:

formats.view.map{format => Try(format.parse(str)).toOption}.find(_.isDefined) 

Try在斯卡拉2.10中引入的。

A view是一种计算懒惰值的集合。它会将Try中的代码应用到收集中的所有项目中,以找到所定义的第一个项目。如果第一个format适用于该字符串,则它不会尝试将其余格式应用于该字符串。

+3

这个答案有两个反模式:1)在尝试抛出意外的异常会丢失,导致它隐藏的错误,并返回不正确的答案(例如,如果在列表中,如果一个视图一个数据库?)ii)过滤器构造一个临时列表,并且还要求访问所有元素,即使只需要第一个元素。它在时间上不必要的昂贵,但特别是在内存中。 – user48956

12

您应该对序列使用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) 
+0

的'find'方法敢于提供完整的工作示例? *编辑*我的意思是有日期的那个。 –

+0

我确实提供了完整的工作示例。盖茨问一般如何找到序列中的第一个元素。 – vitalii

+0

在这里,你去,日期为 – vitalii

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是非线程安全的,这意味着上面的代码不是线程安全的要么!

3

这可以防止不必要的评估。

formats.collectFirst{ case format if Try(format.parse(str)).isSuccess => format.parse(str) } 

parse方法的评价的数量是尝试次数+ 1

2

相同版本使用Scala提取和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 } 
} 
相关问题