2013-06-02 101 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 } 
} 
相關問題