Rory
Rory

Reputation: 838

Any way of simplifying this Scala code (pattern matching)?

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

Answers (2)

Luka Jacobowitz
Luka Jacobowitz

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 checkIntreturns 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

Shane Perry
Shane Perry

Reputation: 990

An alternative to using for-comprehensions 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

Related Questions