Last active
July 18, 2016 15:16
-
-
Save hussachai/7cb359711a84c97afe1d000093921c8d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.LocalDate | |
import java.time.format.DateTimeFormatter | |
import java.time.temporal.TemporalAccessor | |
import org.scalameter.{Key, Warmer, _} | |
import scala.annotation.tailrec | |
import scala.util.{Failure, Success, Try} | |
/** | |
* Created by Hussachai on 7/15/2016. | |
*/ | |
object ParsingDateString extends App { | |
val dateFormats = List( | |
"dd/MM/uuuu", | |
"MMM dd, uuuu", | |
"dd MMMM uuuu", | |
"dd MMM uuuu", | |
"dd-MM-uuuu" | |
).map(p => (p, DateTimeFormatter.ofPattern(p))) ++ (1 to 1000).map{ i => | |
val p = "dd-MM-uuuu."+i | |
(p, DateTimeFormatter.ofPattern("dd-MM-uuuu"+p)) | |
} | |
//Eager collection - It's bad because it tries all the patterns | |
def normalizeDate(dateStr: String): Option[String] = { | |
val trimmedDate = dateStr.trim | |
if(trimmedDate.isEmpty) None | |
else { | |
dateFormats.map { case (pattern, fmt) => | |
Try(fmt.parse(trimmedDate)) | |
}.find(_.isSuccess).map{ t => | |
DateTimeFormatter.ISO_LOCAL_DATE.format(t.get) } | |
} | |
} | |
//Lazy collection | |
def normalizeDate1(dateStr: String): Option[String] = { | |
val trimmedDate = dateStr.trim | |
if(trimmedDate.isEmpty) None | |
else { | |
dateFormats.toStream.map { case (pattern, fmt) => | |
Try(fmt.parse(trimmedDate)) | |
}.find(_.isSuccess).map{ t => | |
DateTimeFormatter.ISO_LOCAL_DATE.format(t.get) } | |
} | |
} | |
//Tail-recursion | |
def normalizeDate2(dateStr: String): Option[String] = { | |
val trimmedDate = dateStr.trim | |
@tailrec | |
def normalize(patterns: List[(String, DateTimeFormatter)]): Try[TemporalAccessor] = patterns match { | |
case head::tail => { | |
val resultTry = Try(head._2.parse(trimmedDate)) | |
if(resultTry.isSuccess) resultTry | |
else normalize(tail) | |
} | |
case _ => Failure(new RuntimeException("no match found")) | |
} | |
if(trimmedDate.isEmpty) None | |
else { | |
normalize(dateFormats).map(DateTimeFormatter.ISO_LOCAL_DATE.format(_)).toOption | |
} | |
} | |
//Imperative | |
def normalizeDate3(dateStr: String): Option[String] = { | |
val trimmedDate = dateStr.trim | |
if(trimmedDate.isEmpty) None | |
else { | |
for((pattern, fmt) <- dateFormats) { | |
val dateTry = Try(fmt.parse(trimmedDate)) | |
if(dateTry.isSuccess){ | |
return Some(DateTimeFormatter.ISO_LOCAL_DATE.format(dateTry.get)) | |
} | |
} | |
None | |
} | |
} | |
//collectFirst with extractor | |
def normalizeDate4(dateStr: String): Option[String] = { | |
object DateExtractor { | |
def unapply(t: (String, DateTimeFormatter)): Option[TemporalAccessor] = Try(t._2.parse(dateStr)).toOption | |
} | |
dateFormats.collectFirst{ | |
case DateExtractor(t) => t | |
}.map { d => | |
DateTimeFormatter.ISO_LOCAL_DATE.format(d) | |
} | |
} | |
val standardConfig = config( | |
Key.exec.minWarmupRuns -> 100, | |
Key.exec.maxWarmupRuns -> 500, | |
Key.exec.benchRuns -> 10000 | |
) withWarmer(new Warmer.Default) withMeasurer(new Measurer.IgnoringGC) | |
println(s"Collection size: ${dateFormats.size}") | |
val time = standardConfig measure { | |
normalizeDate("12 January 2012") | |
} | |
println(s"Eager-Collection: $time ms") | |
val time1 = standardConfig measure { | |
normalizeDate1("12 January 2012") | |
} | |
println(s"Lazy-Collection: $time1 ms") | |
val time2 = standardConfig measure { | |
normalizeDate2("12 January 2012") | |
} | |
println(s"Tail recursion: $time2 ms") | |
val time3 = standardConfig measure { | |
normalizeDate3("12 January 2012") | |
} | |
println(s"Imperative: $time3 ms") | |
val time4 = standardConfig measure { | |
normalizeDate4("12 January 2012") | |
} | |
println(s"collectFirst with extractor: $time4 ms") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment