46

一般に、特定の条件を満たす最初の要素を見つける方法はSeq?

たとえば、可能な日付形式のリストがあり、日付文字列を解析できる最初の 1 つの形式の解析結果を見つけたいとします。

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?)

4

8 に答える 8

33

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)
}

または遅延コレクションを使用します。

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)
于 2013-06-02T14:23:43.960 に答える
25

少なくとも 1 つのフォーマットが成功すると確信している場合:

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

もう少し安全になりたい場合:

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

TryScala 2.10 で導入されました。

Aviewは、値を遅延計算するコレクションのタイプです。Try定義されている最初の項目を見つけるのに必要な数のコレクション内の項目のみに、内のコードが適用されます。最初formatの形式が文字列に適用される場合、残りの形式を文字列に適用しようとしません。

于 2013-06-02T14:48:06.457 に答える
10

これにより、不要な評価が防止されます。

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

メソッドの評価parse回数は、試行回数 + 1 です。

于 2013-06-03T04:29:04.730 に答える
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は非スレッドセーフなので、上記のコードもスレッドセーフではありません!

于 2013-06-02T14:50:38.187 に答える
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))
于 2020-02-20T09:14:12.690 に答える