2013-06-02 9 views
21

Ogólnie, jak znaleźć pierwszy element spełniający określony warunek w Seq?Znajdź pierwszy element, który spełnia warunek X w Seq

Na przykład mam listę możliwego formatu daty i chcę znaleźć analizowany wynik pierwszego formatu, który może przeanalizować mój ciąg dat.

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 

Nieźle. Ale 1. To trochę brzydkie. 2. wykonał niepotrzebną pracę (wypróbował formaty "MM yyyy" i "MM, yyyy"). Być może jest bardziej elegancki i idiomatyczny sposób? (używając Iterator?)

+0

użyć 'find' metody' Seq' – Kakaji

Odpowiedz

13

Jeśli jesteś przekonany, co najmniej jeden format wola uda:

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

Jeśli chcesz być nieco bezpieczniejsze:

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

Try został wprowadzony w Scala 2.10.

A view to rodzaj kolekcji, która leniwie wylicza wartości. Zastosuje kod w ramach Try tylko do tylu pozycji w kolekcji, jakie są niezbędne do znalezienia pierwszego zdefiniowanego. Jeśli pierwszy ciąg znaków odnosi się do format, to nie będzie próbował zastosować pozostałych formatów do łańcucha.

+3

Ta odpowiedź ma dwa wzorce anti: i) nieoczekiwane wyjątki zgłoszone w Try będą utracone, powodując ukrywanie błędów i zwracanie nieprawidłowych odpowiedzi (na przykład, co jeśli lista jeśli widok baza danych?) ii) filtr tworzy listę tymczasową, a także wymaga, aby WSZYSTKIE elementy zostały odwiedzone, nawet jeśli wymagana jest tylko pierwsza. Jest niepotrzebnie drogi w czasie, ale szczególnie w pamięci. – user48956

12

Powinieneś użyć metody find na sekwencjach. Generalnie powinieneś preferować wbudowane metody, ponieważ mogą być zoptymalizowane pod kątem określonej sekwencji.

Console println List(1,2,3,4,5).find(_ == 5) 
res: Some(5) 

Oznacza to, że aby powrócić pierwszy SimpleDateFormat że mecz:

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

Aby powrócić pierwszą datę, która jest przetwarzana:

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

lub korzystania leniwy kolekcji:

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

Odważysz się dać pełny przykład działania? * edytowane * Mam na myśli ten z datami. –

+0

Udostępniłem pełny przykład pracy. Guy pyta, jak ogólnie znaleźć pierwszy element w sekwencji. – vitalii

+0

proszę bardzo, ten z datami – 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) 

Nawiasem mówiąc, ponieważ SimpleDateFormat jest non-thread-safe, co oznacza, że ​​powyższy kod nie jest bezpieczny wątku albo!

3

Zapobiega to niepotrzebnym ocenom.

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

Liczba ocen sposobu parse jest liczba prób + 1.

2

samej wersji z Scala Extractor i 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 } 
} 
Powiązane problemy