2013-06-02 22 views
21

Nói chung, cách tìm phần tử đầu tiên thỏa mãn điều kiện nhất định trong một Seq?Tìm phần tử đầu tiên thỏa mãn điều kiện X trong Seq

Ví dụ: tôi có danh sách định dạng ngày có thể và tôi muốn tìm kết quả được phân tích cú pháp của một định dạng đầu tiên có thể phân tích cú pháp chuỗi ngày của tôi.

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 

Không tệ. Nhưng 1. đó là một chút xấu xí. 2. nó đã làm một số công việc không cần thiết (đã thử các định dạng "MM yyyy""MM, yyyy"). Có lẽ có cách thanh lịch và thành ngữ hơn? (sử dụng Iterator?)

+0

Sử dụng 'phương pháp find' của 'Seq' – Kakaji

Trả lời

13

Nếu bạn tự tin ít nhất một định dạng chí sẽ thành công:

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

Nếu bạn muốn được an toàn hơn một chút:

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

Try được giới thiệu vào Scala 2.10.

A view là một loại bộ sưu tập tính toán giá trị một cách lười biếng. Nó sẽ áp dụng mã trong số Try cho chỉ nhiều mục trong bộ sưu tập là cần thiết để tìm mã đầu tiên được xác định. Nếu format đầu tiên áp dụng cho chuỗi, thì nó sẽ không cố gắng áp dụng các định dạng còn lại cho chuỗi.

+3

Câu trả lời này có hai mẫu chống: i) ngoại lệ không mong muốn được ném trong phần Thử sẽ bị mất, khiến nó ẩn các lỗi và trả về các câu trả lời không chính xác (ví dụ: một cơ sở dữ liệu?) ii) bộ lọc xây dựng một danh sách tạm thời và cũng yêu cầu tất cả các phần tử được truy cập ngay cả khi chỉ có phần tử đầu tiên được yêu cầu. Nó không cần thiết tốn kém trong thời gian, nhưng đặc biệt là trong bộ nhớ. – user48956

12

Bạn nên sử dụng phương thức find theo trình tự. Nói chung, bạn nên thích các phương thức dựng sẵn, vì chúng có thể được tối ưu hóa cho một chuỗi cụ thể.

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

Đó là, để trở về SimpleDateFormat đầu tiên phù hợp:

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

Để trở về ngày đầu tiên mà đã được xử lý:

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

hoặc sử dụng bộ sưu tập lười biếng:

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

Dám cung cấp ví dụ làm việc đầy đủ? * chỉnh sửa * Tôi có nghĩa là một với ngày tháng. –

+0

Tôi đã cung cấp ví dụ làm việc đầy đủ. Guy hỏi làm thế nào thường tìm thấy phần tử đầu tiên trong một chuỗi. – vitalii

+0

ở đây bạn đi, một với ngày – 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) 

Bằng cách này, vì SimpleDateFormat không an toàn với luồng, có nghĩa là mã trên không an toàn ren!

3

Điều này ngăn các đánh giá không cần thiết.

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

Số lượng đánh giá của phương pháp parse là số cố gắng + 1.

2

Cùng phiên bản với Scala Extractor và 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 } 
} 
Các vấn đề liên quan