Reputation: 838
I have some values provided by the user as Option[String]
. I want to validate them only if they are non-empty.
The validation just checks that the string can be converted to an int, and is not less that 0.
Is there any way of simplifying this code, or making it more readable?
val top: Option[String] = ...
val skip: Option[String] = ...
val validationErrors = new ListBuffer[Err]()
top match {
case Some(x) => if (x.toIntOpt.isEmpty || x.toIntOpt.get < 0) validationErrors += PositiveIntegerRequired("$top")
}
skip match {
case Some(x) => if (x.toIntOpt.isEmpty || x.toIntOpt.get < 0) validationErrors += PositiveIntegerRequired("$skip")
}
And here is the toIntOpt helper:
def toIntOpt: Option[Int] = Try(s.toInt).toOption
Upvotes: 0
Views: 174
Reputation: 23532
Yes it can be simplified a lot, by making use of flatMap
and for-comprehensions
and collect
:
def checkInt[A](stringOpt: Option[String], a: A): Option[A] = for {
s <- stringOpt
i <- s.toIntOpt if (i < 0)
} yield a
val validationErrors = List(
checkInt(top, PositiveIntegerRequired("$top")),
checkInt(skip, PositiveIntegerRequired("$skip"))
).collect {
case Some(x) => x
}
The first function checkInt
returns a value a
if the original Option
was non-empty and contained an invalid negative integer.
Then we put both into a List and collect only the values that are non-empty, resulting in a List
of Err
with no need for creating an intermediate Buffer
.
An even easier way to do something like this can be found with the Validated
type found in the cats library: https://typelevel.org/cats/datatypes/validated.html
Upvotes: 3
Reputation: 990
An alternative to using for-comprehension
s is to use a map
with a filter
.
top.map(toIntOpt)
.filter(i => i.getOrElse(-1) < 0)
.foreach(_ => validationErrors += PositiveIntegerRequired("$top"))
The map
call will result in a Option[Option[Int]]
which is then filtered based on the value of the nested Option
(defaulting to -1
if the option is None
).
Upvotes: 0