I have the following implementation:
val dateFormats = Seq("dd/MM/yyyy", "dd.MM.yyyy")
implicit def dateTimeCSVConverter: CsvFieldReader[DateTime] = (s: String) => Try {
val elem = dateFormats.map {
format =>
try {
Some(DateTimeFormat.forPattern(format).parseDateTime(s))
} catch {
case _: IllegalArgumentException =>
None
}
}.collectFirst {
case e if e.isDefined => e.get
}
if (elem.isDefined)
elem.get
else
throw new IllegalArgumentException(s"Unable to parse DateTime $s")
}
So basically what I'm doing is that, I'm running over my Seq and trying to parse the DateTime with different formats. I then collect the first one that succeeds and if not I throw the Exception back.
I'm not completely satisfied with the code. Is there a better way to make it simpler? I need the exception message passed on to the caller.
The one problem with your code is it tries all patterns no matter if date was already parsed. You could use lazy collection, like Stream to solve this problem:
def dateTimeCSVConverter(s: String) = Stream("dd/MM/yyyy", "dd.MM.yyyy")
.map(f => Try(DateTimeFormat.forPattern(format).parseDateTime(s))
.dropWhile(_.isFailure)
.headOption
Even better is the solution proposed by jwvh with find (you don't have to call headOption):
def dateTimeCSVConverter(s: String) = Stream("dd/MM/yyyy", "dd.MM.yyyy")
.map(f => Try(DateTimeFormat.forPattern(format).parseDateTime(s))
.find(_.isSuccess)
It returns None if none of patterns matched. If you want to throw exception on that case, you can uwrap option with getOrElse:
...
.dropWhile(_.isFailure)
.headOption
.getOrElse(throw new IllegalArgumentException(s"Unable to parse DateTime $s"))
The important thing is, that when any validation succeedes, it won't go further but will return parsed date right away.
This is a possible solution that iterates through all the options
val dateFormats = Seq("dd/MM/yyyy", "dd.MM.yyyy")
val dates = Vector("01/01/2019", "01.01.2019", "01-01-2019")
dates.foreach(s => {
val d: Option[Try[DateTime]] = dateFormats
.map(format => Try(DateTimeFormat.forPattern(format).parseDateTime(s)))
.filter(_.isSuccess)
.headOption
d match {
case Some(d) => println(d.toString)
case _ => throw new IllegalArgumentException("foo")
}
})
This is an alternative solution that returns the first successful conversion, if any
val dateFormats = Seq("dd/MM/yyyy", "dd.MM.yyyy")
val dates = Vector("01/01/2019", "01.01.2019", "01-01-2019")
dates.foreach(s => {
dateFormats.find(format => Try(DateTimeFormat.forPattern(format).parseDateTime(s)).isSuccess) match {
case Some(format) => println(DateTimeFormat.forPattern(format).parseDateTime(s))
case _ => throw new IllegalArgumentException("foo")
}
})
I made it sweet like this now! I like this a lot better! Use this if you want to collect all the successes and all the failures. Note that, this might be a bit in-efficient when you need to break out of the loop as soon as you find one success!
implicit def dateTimeCSVConverter: CsvFieldReader[DateTime] = (s: String) => Try {
val (successes, failures) = dateFormats.map {
case format => Try(DateTimeFormat.forPattern(format).parseDateTime(s))
}.partition(_.isSuccess)
if (successes.nonEmpty)
successes.head.get
else
failures.head.get
}
Related
I have a sequence of left and right values like:
val l: Seq[Either[Error, Data]] = Seq(Left(Error), Right(Data), ...)
I want to map all Right values and display the error for a Left.
I have tried:
val data: Seq[Data] = l.flatMap {
case Right(data) => data
case Left(err) => println(err) // doesn't work because println is Unit
}
Any way to do this?
It's generally not a great practice to mix side effects and pure code like this, but something like (assuming a strict Seq):
def rightsAfterEffectingLefts[A, B](eithers: Seq[Either[A, B]])(effect: A => Unit): Seq[B] = {
eithers.foreach(_.left.foreach(effect))
eithers.flatMap(_.toOption)
}
val data = rightsAfterEffectingLefts(l)(println _)
It's possible to optimize to avoid the double iteration, though you'd likely want to approach different Seq implementations differently.
EDIT: after Luis's suggestion
def rightsAfterEffectingLefts[A, B](eithers: Seq[Either[A, B]])(effect: A => Unit): Seq[B] = {
val (lefts, rights) = eithers.partition(_.isLeft)
lefts.foreach(_.left.foreach(effect))
rights.flatMap(_.toOption)
}
is an alternative definition. It still double iterates and will likely be slower.
You were on the right track, just need to let flatMap() remove the Error after it's been printed.
val data: Seq[Data] = l.flatMap {
case Right(data) => Some(data)
case Left(err) => println(err); None
}
A single traversal is all that's needed.
I have a string that I need to convert into a long if it is not empty or blank. If it either can't be converted(it fails during conversion) or the string is empty or blank I need to run a secondary function. Right now I have a if statement but I would like to convert it to pattern matching if possible to make my code a little cleaner.
Heres what I have right now
val stringToConvert = "1234"
if (!stringToConvert.isBlank && !stringToConvert.isEmpty) {
try {
val converted = stringToConvert.toLong
doThingWithConvertedValue(converted)
}
catch {
case _: Throwable =>
Log.error("Encountered error while trying to convert string to long")
}
}
else {
doOtherThing()
}
Whats the best way to do this?
Try this:
import scala.util.{Try, Success, Failure}
val stringToConvert = "1234"
Try(stringToConvert.toLong) match {
case Success(value) => doThingWithConvertedValue(value)
case Failure(_) if stringToConvert.isBlank => doOtherThing()
case Failure(exception) =>
Log.error(s"Encounted error while trying to convert string to long: $exception")
}
Please note that isBlank already covers isEmpty case.
Considering the following functions:
def doThingWithConvertedValue(l: Long) = println (s"doThingWithConvertedValue($l)")
def doOtherThing() = println ("doOtherThing()")
Your code can be transferred to the following:
Try(stringToConvert).filter(stringToConvert => !stringToConvert.isBlank && stringToConvert.nonEmpty).fold(
sBlankOrEmpty => doOtherThing(),
nEmpty => Try(doThingWithConvertedValue(nEmpty.toLong)).getOrElse {
Log.error("Encountered error while trying to convert string to long")
}
)
Outputs:
"1234" => doThingWithConvertedValue(1234)
"notConvertable" => Encountered error while trying to convert string to long
"" => doOtherThing()
If you prefer using Pattern matching, you can do it as follows:
val stringToConvert = "123s"
stringToConvert match {
case sBlankOrEmpty if sBlankOrEmpty.isBlank || sBlankOrEmpty.isEmpty => doOtherThing()
case canBeLong if Try(canBeLong.toLong).isSuccess => doThingWithConvertedValue(canBeLong.toLong)
case _ => Log.error("Encountered error while trying to convert string to long")
}
Note:
doThingWithConvertedValue can throw an exception (which you didnt recover properly for that case in your main file)
.toLong is used twice O(n) Each, but if its fine for you i guess its pretty
readable
I would do something like this:
val stringToConvert = "1234"
Option(stringToConvert)
.map(_.trim)
.filter(_.isEmpty)
.fold(ifEmpty = doOtherThing() { str =>
trs
.toLongOption
.fold(ifEmpty = Log.error("Encounted error while trying to convert string to long"))
(doThingWithConvertedValue)
}
I'm going to suggest that you don't need a pattern match. Just fold() over a Try.
util.Try(stringToConvert.toLong)
.fold(exptn => {
//Log.error() and doOtherThing() go here depending on requirements
if (stringToConvert.isEmpty) ... //only when empty
else ... //only when non-empty
... //do this regardless of empty status
},doThingWithConvertedValue)
You can do:
val stringToConvert = "1234"
Try(doThingWithConvertedValue(stringToConvert.toLong)).getOrElse {
println("Encounted error while trying to convert string to long")
doOtherThing()
}
Code run at Scastie.
I've got a problem with returning a list after handling futures in scala. My code looks like this:
def getElements(arrayOfIds: Future[Seq[Int]]): Future[Seq[Element]] = {
var elementArray: Seq[Element] = Seq()
arrayOfIds.map {
ids => ids.map(id => dto.getElementById(id).map {
case Some(element) => elementArray = elementArray :+ element
case None => println("Element not found")
})
}
arrayOfIds.onComplete(_ => elementArray)
}
I'd like to do something like .onComplete, however the return type is
Unit and I'd like to return a Future[Seq[Whatever]]. Is there clean way to handle futures like this? Thanks!
Please provide the type of function dto.getElementById. If it is Int => Future[Option[Element]], then:
def getElements(arrayOfIds: Future[Seq[Int]]): Future[Seq[Element]] = {
val allElements: Future[Seq[Option[Element]]] = arrayOfIds.flatMap( ids =>
Future.sequence(ids.map(dto.getElementById))
)
allElements.map(_.flatMap{
case None => println();None
case some => some
})
}
Without logging, it would be:
arrayOfIds.flatMap( ids => Future.traverse(ids.map(dto.getElementById))(_.flatten))
Instead of assigning the result to a mutable variable, return it from the continuation of the Future. You can use flatMap to extract only the Element results which actually contain a value:
def getElements(arrayOfIds: Future[Seq[Int]]): Future[Seq[Element]] = {
arrayOfIds.flatMap(id => Future.fold(id.map(getElementById))(Seq.empty[Element])(_ ++ _))
}
effective scala style states the following:
this is both succinct and correct, but nearly every reader will have a
difficult time recovering the original intent of the author. A
strategy that often serves to clarify is to name intermediate results
and parameters:
val votesByLang = votes groupBy { case (lang, _) => lang }
val sumByLang = votesByLang map { case (lang, counts) =>
val countsOnly = counts map { case (_, count) => count }
(lang, countsOnly.sum)
}
val orderedVotes = sumByLang.toSeq
.sortBy { case (_, count) => count }
.reverse
what I fail to understand is in this style if i have errors while evaluating the value for votesByLang, sumByLang, ... how can I have a single recover at the end after all if i just had .map.map ... I could have a single recover at the end. So is it possible to have a single recover also with this style? (and not only possible but with good style...)
Or in other words what is the succinct and correct error handling in this succinct and correct functional pipelining code?
you see with the non-concise version i'm able to code this:
object MyRealMainObj extends App {
println(
Try(1)
.map(doOne)
.map(doTwo)
.recover { // non succinct version can catch any prior error how to achieve the same in succinct version
case e: Throwable => println("recovering from: " + e.getMessage)
}
)
def doOne(i: Int): Int = { i + 1; throw new RuntimeException("failed in one") }
def doTwo(i: Int): Int = { i + 2; throw new RuntimeException("failed in two") }
}
a single recover will catch any previous error in the .map but with the concise-succinct how can I achieve the same in concise and succinct way?
It sound like you expect some sort of scope to be magically created by .recover. This is not the case. With some minor simplifications, Try(expr) is
try {
val result = expr
Success(result)
catch {
e => Failure(e)
}
tryResult.map(f) is :
tryResult match {
case Success(x) => Try(f(x))
case f: Failure => f
}
tryResult.recover(f) is
tryResult match {
case Failure(e) if f.isDefinedAt(e) => Try(f(e))
case other => other
}
There is no special magic. At every stage, you have values, not code in the scope of some try/catch. Putting those values in val will not change a thing. If you "debug by hand", you will notice that you get a Success object, then at some point, one of your map may fail and return a Failure, the following ones will keep being failures, and then when you pass the last one to recover, it may go back to Success. Settings vals in between cannot make any difference.
I have some code for validating ip addresses that looks like the following:
sealed abstract class Result
case object Valid extends Result
case class Malformatted(val invalid: Iterable[IpConfig]) extends Result
case class Duplicates(val dups: Iterable[Inet4Address]) extends Result
case class Unavailable(val taken: Iterable[Inet4Address]) extends Result
def result(ipConfigs: Iterable[IpConfig]): Result = {
val invalidIpConfigs: Iterable[IpConfig] =
ipConfigs.filterNot(ipConfig => {
(isValidIpv4(ipConfig.address)
&& isValidIpv4(ipConfig.gateway))
})
if (!invalidIpConfigs.isEmpty) {
Malformatted(invalidIpConfigs)
} else {
val ipv4it: Iterable[Inet4Address] = ipConfigs.map { ipConfig =>
InetAddress.getByName(ipConfig.address).asInstanceOf[Inet4Address]
}
val dups = ipv4it.groupBy(identity).filter(_._2.size != 1).keys
if (!dups.isEmpty) {
Duplicates(dups)
} else {
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
val taken: Iterable[Inet4Address] = ipAvailability.filter(!_._2).keys
if (!taken.isEmpty) {
Unavailable(taken)
} else {
Valid
}
}
}
}
I don't like the nested ifs because it makes the code less readable. Is there a nice way to linearize this code? In java, I might use return statements, but this is discouraged in scala.
I personally advocate using a match everywhere you can, as it in my opinion usually makes code very readable
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil =>
val ipv4it = ipConfigs.map { ipc =>
InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address]
}
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil =>
val taken = ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys
if (taken.nonEmpty) Unavailable(taken) else Valid
case dups => Duplicates(dups)
}
case invalid => Malformatted(invalid)
}
Note that I've chosen to match on the else part first, since you generally go from specific to generic in matches, since Nil is a subclass of Iterable I put that as the first case, eliminating the need for an i if i.nonEmpty in the other case, since it would be a given if it didn't match Nil
Also a thing to note here, all your vals don't need the type explicitly defined, it significantly declutters the code if you write something like
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
as simply
val ipAvailability = ipv4it.map(ip => (ip, isIpAvailable(ip)))
I've also taken the liberty of removing many one-off variables I didn't find remotely necessary, as all they did was add more lines to the code
A thing to note here about using match over nested ifs, is that is that it's easier to add a new case than it is to add a new else if 99% of the time, thereby making it more modular, and modularity is always a good thing.
Alternatively, as suggested by Nathaniel Ford, you can break it up into several smaller methods, in which case the above code would look like so:
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil => wellFormatted(ipConfigs)
case i => Malformatted(i)
}
def wellFormatted(ipConfigs: Iterable[IpConfig]): Result = {
val ipv4it = ipConfigs.map(ipc => InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address])
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil => noDuplicates(ipv4it)
case dups => Duplicates(dups)
}
}
def noDuplicates(ipv4it: Iterable[IpConfig]): Result =
ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys match {
case Nil => Valid
case taken => Unavailable(taken)
}
This has the benefit of splitting it up into smaller more manageable chunks, while keeping to the FP ideal of having functions that only do one thing, but do that one thing well, rather than having god-methods that do everything.
Which style you prefer, of course is up to you.
This has some time now but I will add my 2 cents. The proper way to handle this is with Either. You can create a method like:
def checkErrors[T](errorList: Iterable[T], onError: Result) : Either[Result, Unit] = if(errorList.isEmpty) Right() else Left(onError)
so you can use for comprehension syntax
val invalidIpConfigs = getFormatErrors(ipConfigs)
val result = for {
_ <- checkErrors(invalidIpConfigs, Malformatted(invalidIpConfigs))
dups = getDuplicates(ipConfigs)
_ <- checkErrors(dups, Duplicates(dups))
taken = getAvailability(ipConfigs)
_ <- checkErrors(taken, Unavailable(taken))
} yield Valid
If you don't want to return an Either use
result.fold(l => l, r => r)
In case of the check methods uses Futures (could be the case for getAvailability, for example), you can use cats library to be able of use it in a clean way: https://typelevel.org/cats/datatypes/eithert.html
I think it's pretty readable and I wouldn't try to improve it from there, except that !isEmpty equals to nonEmpty.