Validation that returns more than 1 error - scala

I'm doing a few validations using an older Scala version (2.0) but for each record I am currently getting only 1 error, assuming the record has 2 or more error -- I want to receive everything wrong with that record/item
case class Response(request: JsValue,
success: Boolean,
column: Option[String] = None,
message: String = "")
Here is validator, that return all errors in a json object
def validator(asJson: JsValue): Response = {
if (name == "")
return Response(asJson, false, Some("name"), "Name can't be blank")
if (age == -1 || age == "N/A")
return Response(asJson, false, Some("age"), "Age can't be blank")
if (DOB == -1 || DOB == "N/A" )
return Response(asJson, false, Some("DOB"), "DOB cannot be blank")
else
Response(asJson, true)
}
Currently if record1 doesn't have a name + age + DOB ---> I'm only getting "Name can't be blank"
How do I get (multiple errors per item instead of just 1 error per item):
Name can't be blank, Age can't be blank, DOB cannot be blank

This is just an outline of how it might work:
def validator(asJson: JsValue): Response = {
val errors = List(
if (name == "" ) Some("Name can't be blank") else None,
if (age == -1 || age == "N/A") Some("Age can't be blank") else None,
if (DOB == -1 || DOB == "N/A" ) Some("DOB cannot be blank") else None,
).flatten
if (errors.isEmpty) {
Response(asJson, true)
} else {
Response(asJson, false, errors.mkString(", "))
}
}
The errors value is created by making a List of Option values, one for each validation check. The flatten method extracts the contents of all the Some values and removes any None values. The result is a list of error strings.
The rest of the code formats the Response based on the list of errors.
If you are using Scala 2.13 then Option.when makes the tests shorter and clearer:
Option.when(name == "")("Name can't be blank"),
Option.when(age == -1 || age == "N/A")("Age can't be blank"),
Option.when(DOB == -1 || DOB == "N/A")("DOB cannot be blank"),

Here is an alternative using Validated that you might consider at some point
import play.api.libs.json._
import cats.data.ValidatedNec
import cats.implicits._
case class User(name: String, age: Int, dob: String)
case class UserDTO(name: Option[String], age: Option[Int], dob: Option[String])
implicit val userDtoFormat = Json.format[UserDTO]
val raw =
"""
|{
| "name": "Picard"
|}
|""".stripMargin
val userDto = Json.parse(raw).as[UserDTO]
def validateUser(userDto: UserDTO): ValidatedNec[String, User] = {
def validateName(user: UserDTO): ValidatedNec[String, String] =
user.name.map(_.validNec).getOrElse("Name is empty".invalidNec)
def validateAge(user: UserDTO): ValidatedNec[String, Int] =
user.age.map(_.validNec).getOrElse("Age is empty".invalidNec)
def validateDob(user: UserDTO): ValidatedNec[String, String] =
user.dob.map(_.validNec).getOrElse("DOB is empty".invalidNec)
(validateName(userDto), validateAge(userDto), validateDob(userDto)).mapN(User)
}
validateUser(userDto)
// res0: cats.data.ValidatedNec[String,User] = Invalid(Chain(Age is empty, DOB is empty))
Note the distinction between UserDTO ("data transfer object") which models whatever payload was sent over the wire, and User which models the actual data we require for our business logic. We separate the concern of user validation into its own method validateUser. Now we can work with valid user or errors like so
validateUserDTO(userDto) match {
case Valid(user) =>
// do something with valid user
case Invalid(errors) =>
// do something with errors
}

First of all, in scala you do not have the return statement, the last statement is returned (in fact it exists but its use is not recomended).
Secondly, in your code, I suppose you want to pass through several if statements. But if you return after each if statement you exit your function at the first condition that is true, and you will never go to the rest of the code.
If you want to gather several instances of Response, just gather them in a collection, and return the collection at the end of the function. A collection might be a List, or a Seq, or whatever.

In my code I've simple Validator that does exactly that:
abstract class Validator[T, ERR >: Null] {
def validate(a:T):Seq[ERR]
protected def is(errorCase:Boolean, err: => ERR) = if (errorCase) err else null
protected def check(checks:ERR*) = checks.collectFirst{ case x if x != null => x }.getOrElse(null)
protected def checkAll(checks:ERR*) = checks.filter(_ != null)
}
//implement checks on some types
val passwordValidator = new Validator[String, String] {
val universalPasswordInstruction = "Password needs to have at least one small letter, big letter, number and other sign."
def validate(a: String) = checkAll( //here we check all cases
is(a.length < 10, "Password should be longer than 10"),
check( //here we check only first one.
is(a.forall(_.isLetter), s"Passowrd has only letters. $universalPasswordInstruction"),
is(a.forall(_.isLetterOrDigit), s"Use at least one ascii character like '#' or '*'. $universalPasswordInstruction"),
),
is(a.contains("poop"), "Really!")
)
}
val userValidator = new Validator[(String, String), (Int, String)] {
override def validate(a: (String, String)): Seq[(Int, String)] = checkAll(
is(a._1.length > 0, (0, "Username is empty!")),
) ++ passwordValidator.validate(a._2).map(x => (1, x))
}
//use
userValidator.validate("SomeUserName" -> "SomePassword") match {
case Seq() => //OK case
case errors => //deal with it
}
Maybe not so fancy but works for me :). Code is simple and rather self explanatory. Nulls here are just implementation detail (they doesn't show up in usercode, and can be switched to Options).

Related

Using a predicate to search through a string - Scala

I'm having difficulty figuring out how to search through a string with a given predicate and determining its position in the string.
def find(x: Char => Boolean): Boolean = {
}
Example, if x is (_ == ' ')
String = "hi my name is"
It would add 2 to the counter and return true
I'm guessing that this is what you want...
Since find is a higher-order function (HOF) - that is, it's a function that takes a function as an argument - it likely needs to be applied to a String instance. The predicate (the function argument to find) determines when the character you're looking for is found, and the find method reports the position at which the character was found. So find should return an Option[Int], not a Boolean, that way you don't lose the information about where the character was found. Note that you can still change an Option[Int] result to a Boolean value (with true indicating the search was successful, false not) by applying .isDefined to the result.
Note that I've renamed find to myFind to avoid a clash with the built-in String.find method (which does a similar job).
import scala.annotation.tailrec
// Implicit class cannot be a top-level element, so it's put in an object.
object StringUtils {
// "Decorate" strings with additional functions.
final implicit class MyRichString(val s: String)
extends AnyVal {
// Find a character satisfying predicate p, report position.
def myFind(p: Char => Boolean): Option[Int] = {
// Helper function to keep track of current position.
#tailrec
def currentPos(pos: Int): Option[Int] = {
// If we've passed the end of the string, return None. Didn't find a
// character satisfying predicate.
if(pos >= s.length) None
// Otherwise, if the predicate passes for the current character,
// return position wrapped in Some.
else if(p(s(pos))) Some(pos)
// Otherwise, perform another iteration, looking at the next character.
else currentPos(pos + 1)
}
// Start by looking at the first (0th) character.
currentPos(0)
}
}
}
import StringUtils._
val myString = "hi my name is"
myString.myFind(_ == ' ') // Should report Some(2)
myString.myFind(_ == ' ').isDefined // Should report true
myString.myFind(_ == 'X') // Should report None
myString.myFind(_ == 'X').isDefined // Should report false
If the use of an implicit class is a little too much effort, you could implement this as a single function that takes the String as an argument:
def find(s: String, p: Char => Boolean): Option[Int] = {
// Helper function to keep track of current position.
#tailrec
def currentPos(pos: Int): Option[Int] = {
// If we've passed the end of the string, return None. Didn't find a
// character satisfying predicate.
if(pos >= s.length) None
// Otherwise, if the predicate passes for the current character,
// return position wrapped in Some.
else if(p(s(pos))) Some(pos)
// Otherwise, perform another iteration, looking at the next character.
else currentPos(pos + 1)
}
// Start by looking at the first (0th) character.
currentPos(0)
}
val myString = "hi my name is"
find(myString, _ == ' ') // Should report Some(2)
find(myString, _ == ' ').isDefined // Should report true
find(myString, _ == 'X') // Should report None
find(myString, _ == 'X').isDefined // Should report false
Counter:
"hi my name is".count (_ == 'm')
"hi my name is".toList.filter (_ == 'i').size
Boolean:
"hi my name is".toList.exists (_ == 'i')
"hi my name is".contains ('j')
Position(s):
"hi my name is".zipWithIndex.filter {case (a, b) => a == 'i'}
res8: scala.collection.immutable.IndexedSeq[(Char, Int)] = Vector((i,1), (i,11))
Usage of find:
scala> "hi my name is".find (_ == 'x')
res27: Option[Char] = None
scala> "hi my name is".find (_ == 's')
res28: Option[Char] = Some(s)
I would suggest separating the character search and position into individual methods, each of which leverages built-in functions in String, and wrap in an implicit class:
object MyStringOps {
implicit class CharInString(s: String) {
def charPos(c: Char): Int = s.indexOf(c)
def charFind(p: Char => Boolean): Boolean =
s.find(p) match {
case Some(_) => true
case None => false
}
}
}
import MyStringOps._
"hi my name is".charPos(' ')
// res1: Int = 2
"hi my name is".charFind(_ == ' ')
// res2: Boolean = true

passing variable argument data type to a scala method

I want to create a method/function in scala which can take variable arguments of type string or int and return the result of type String or Int.
def Hello(name: String, Param: int*/string*): Int/String= {
var index = 0
while(index < Param.length) {
var n = name
var ar = Param(index)
if ( n.equals(ar) ) return Param(index + 1)
else index = index + 1
}
return Param(index.length -1)
}
If we call the Hello function then it should return the result as given below.
val Res1 = Hello("Jack", "rakesh", 1, "Jack", 2, "No one")
println(Res1)
=> this should return value 2
val Res2 = Hello("ABC", "rakesh", 1, "Jack", 2, "Vik", 3, "ram", 4, "No one")
println(Res2)
=> this should return value "No one"
Using Any should work:
def hello(name: Any, param: Any*): Any= {
var list = param.dropWhile(_ != name)
list.drop(1).headOption.orElse(param.lastOption).getOrElse("")
}
Depending on how type safe you want it to be, you can try to use generics or other means to restrict the types used. Or you can just pattern match the response type:
hello("ABC", "rakesh", 1, "Jack", 2, "Vik", 3, "ram", 4, "No one") match {
case i: Int => println("Got a int:" + i)
case s: String=> println("Got a string:" + s)
}
This will help you
def Hello( name: String,args: Any* ) = {
val index = args.indexOf(name)
if(index == -1)
args(args.length - 1)
else
args(index + 1)
}
Your whole approach is faulty, but here's how it can be done in a type-safe manner.
def Hello(name: String, param: Either[Int,String]*): Either[Int,String] = {
param.sliding(2,2)
.find(_(0).fold(_ => false, _ == name))
.fold(param.last)(_(1))
}
Usage:
Hello("Jack", Right("rakesh"), Left(1), Right("Jack"), Left(2), Right("No one"))
// res0: Either[Int,String] = Left(2)
Hello("ABC", Right("rakesh"), Left(1), Right("Jack"), Left(2),
Right("Vik"), Left(3), Right("ram"), Left(4), Right("No one"))
// res1: Either[Int,String] = Right(No one)
But it would be better to rethink it from the ground up.
I believe, what you want to achieve, is to get an index of a String element(if start counting from 1) in varargs, or return "No one". No need to pass indices to the method. You can do it like this:
def hello(name: String, params: String*): Any = {
val idx = params.indexOf(name)
if (idx != -1) idx + 1 else "No One"
}
Unfortunately both this:
def Hello(name: String, args: Any* ) = {
val index = args.indexOf(name)
if(index == -1)
args(args.length - 1)
else
args(index + 1)
}
and this:
def hello(name: String, param: Any*): Any= {
var index = 0
while(index < param.length) {
var n = name
var ar = param(index)
if ( n.equals(ar) ) return param(index + 1)
else index = index + 1
}
param(index -1)
}
are broken, as they throw an exception if you try to find the index of "No one", as index + 1 will equal to the size of the array. And it's better to compare things in Scala with == for logical equality.
But it's better not to return Any at all, but return Option[Int]:
def hello(name: String, params: String*): Option[Int] =
Option(params.indexOf(name)).filter(_ != -1).map(_ + 1)
So then you can use it like this:
val message1 = hello("Jack", "rakesh" ,"Jack").getOrElse("No one")
val message2 = hello("ABC", "rakesh", "Jack", "Vik", "ram").getOrElse("No one")
Answering the comment:
I want to know how can i pass mixed datatypes to "param".
The simplest way is to have them all of type Any
and also get string or integer as return type
The same way, defining return type as Any
The only small issue here, is that there will be no compile time check against other types. E.g. somebody might pass Boolean or any complex object along with String's and Int's to your function. But you can check at runtime against it or play with types to limit them. I don't know your requirement here, maybe it's even advantage for you.
If having Any is fine, then I would solve it like this:
def Hello(name: Any, params: Any*): Any = Option(params)
.withFilter(_.nonEmpty)
.map(_.indexOf(name))
.filter(i => i != -1 && i < params.length - 1)
.map(i => params(i + 1))
.getOrElse("No one")
Or, if you can assume, params are never empty and you have to use the last param as the default, instead of just hard coded "No one":
def Hello(name: Any, params: Any*): Any = Option(params)
.withFilter(_.nonEmpty)
.map(_.indexOf(name))
.filter(i => i != -1 && i < params.length - 1)
.map(i => params(i + 1))
.getOrElse(params.last)
Notice the check against "No one" attack: i < params.length - 1.
Notice that name now is also of type Any.
Now, even if you pass "No one" as a name, the Option will evaluate to None thanking to the filter, and getOrElse will give you the default "No one" instead of an exception.

Conditionally map a nullable field to None value in Slick

In Slick 2.1, I need to perform a query/map operation where I convert a nullable field to None if it contains a certain non-null value. Not sure whether it matters or not, but in my case the column type in question is a mapped column type. Here is a code snippet which tries to illustrate what I'm trying to do. It won't compile, as the compiler doesn't like the None.
case class Record(field1: Int, field2: Int, field3: MyEnum)
sealed trait MyEnum
val MyValue: MyEnum = new MyEnum { }
// table is a TableQuery[Record]
table.map { r => (
r.field1,
r.field2,
Case If (r.field3 === MyValue) Then MyValue Else None // compile error on 'None'
)
}
The error is something like this:
type mismatch; found : None.type required: scala.slick.lifted.Column[MyEnum]
Actually, the reason I want to do this is that I want to perform a groupBy in which I count the number of records whose field3 contains a given value. I couldn't get the more complicated groupBy expression working, so I backed off to this simpler example which I still can't get working. If there's a more direct way to show me the groupBy expression, that would be fine too. Thanks!
Update
I tried the code suggested by #cvogt but this produces a compile error. Here is a SSCCE in case anyone can spot what I'm doing wrong here. Compile fails with "value ? is not a member of Int":
import scala.slick.jdbc.JdbcBackend.Database
import scala.slick.driver.H2Driver
object ExpMain extends App {
val dbName = "mydb"
val db = Database.forURL(s"jdbc:h2:mem:${dbName};DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
val driver = H2Driver
import driver.simple._
class Exp(tag: Tag) extends Table[(Int, Option[Int])](tag, "EXP") {
def id = column[Int]("ID", O.PrimaryKey)
def value = column[Option[Int]]("VALUE")
def * = (id, value)
}
val exp = TableQuery[Exp]
db withSession { implicit session =>
exp.ddl.create
exp += (1, (Some(1)))
exp += (2, None)
exp += (3, (Some(4)))
exp.map { record =>
Case If (record.value === 1) Then 1.? Else None // this will NOT compile
//Case If (record.value === 1) Then Some(1) Else None // this will NOT compile
//Case If (record.value === 1) Then 1 Else 0 // this will compile
}.foreach {
println
}
}
}
I need to perform a query/map operation where I convert a nullable field to None if it contains a certain non-null value
Given the example data you have in the update, and pretending that 1 is the "certain" value you care about, I believe this is the output you expect:
None, None, Some(4)
(for rows with IDs 1, 2 and 3)
If I've understood the problem correctly, is this what you need...?
val q: Query[Column[Option[Int]], Option[Int], Seq] = exp.map { record =>
Case If (record.value === 1) Then (None: Option[Int]) Else (record.value)
}
...which equates to:
select (case when ("VALUE" = 1) then null else "VALUE" end) from "EXP"
You need to wrap MyValue in an Option, so that both outcomes of the conditional are options. In Slick 2.1 you use the .? operator for that. In Slick 3.0 it will likely be Rep.Some(...).
Try
Case If (r.field3 === MyValue) Then MyValue.? Else None
or
Case If (r.field3 === MyValue) Then MyValue.? Else (None: Option[MyEnum])

Filtering inside `for` with pattern matching

I am reading a TSV file and using using something like this:
case class Entry(entryType: Int, value: Int)
def filterEntries(): Iterator[Entry] = {
for {
line <- scala.io.Source.fromFile("filename").getLines()
} yield new Entry(line.split("\t").map(x => x.toInt))
}
Now I am both interested in filtering out entries whose entryType are set to 0 and ignoring lines with column count greater or lesser than 2 (that does not match the constructor). I was wondering if there's an idiomatic way to achieve this may be using pattern matching and unapply method in a companion object. The only thing I can think of is using .filter on the resulting iterator.
I will also accept solution not involving for loop but that returns Iterator[Entry]. They solutions must be tolerant to malformed inputs.
This is more state-of-arty:
package object liner {
implicit class R(val sc: StringContext) {
object r {
def unapplySeq(s: String): Option[Seq[String]] = sc.parts.mkString.r unapplySeq s
}
}
}
package liner {
case class Entry(entryType: Int, value: Int)
object I {
def unapply(s: String): Option[Int] = util.Try(s.toInt).toOption
}
object Test extends App {
def lines = List("1 2", "3", "", " 4 5 ", "junk", "0, 100000", "6 7 8")
def entries = lines flatMap {
case r"""\s*${I(i)}(\d+)\s+${I(j)}(\d+)\s*""" if i != 0 => Some(Entry(i, j))
case __________________________________________________ => None
}
Console println entries
}
}
Hopefully, the regex interpolator will make it into the standard distro soon, but this shows how easy it is to rig up. Also hopefully, a scanf-style interpolator will allow easy extraction with case f"$i%d".
I just started using the "elongated wildcard" in patterns to align the arrows.
There is a pupal or maybe larval regex macro:
https://github.com/som-snytt/regextractor
You can create variables in the head of the for-comprehension and then use a guard:
edit: ensure length of array
for {
line <- scala.io.Source.fromFile("filename").getLines()
arr = line.split("\t").map(x => x.toInt)
if arr.size == 2 && arr(0) != 0
} yield new Entry(arr(0), arr(1))
I have solved it using the following code:
import scala.util.{Try, Success}
val lines = List(
"1\t2",
"1\t",
"2",
"hello",
"1\t3"
)
case class Entry(val entryType: Int, val value: Int)
object Entry {
def unapply(line: String) = {
line.split("\t").map(x => Try(x.toInt)) match {
case Array(Success(entryType: Int), Success(value: Int)) => Some(Entry(entryType, value))
case _ =>
println("Malformed line: " + line)
None
}
}
}
for {
line <- lines
entryOption = Entry.unapply(line)
if entryOption.isDefined
} yield entryOption.get
The left hand side of a <- or = in a for-loop may be a fully-fledged pattern. So you may write this:
def filterEntries(): Iterator[Int] = for {
line <- scala.io.Source.fromFile("filename").getLines()
arr = line.split("\t").map(x => x.toInt)
if arr.size == 2
// now you may use pattern matching to extract the array
Array(entryType, value) = arr
if entryType == 0
} yield Entry(entryType, value)
Note that this solution will throw a NumberFormatException if a field is not convertible to an Int. If you do not want that, you'll have to encapsulate x.toInt with a Try and pattern match again.

Is there any way to use immutable collections here + make the code look better?

I have to validate some variables manually for some reason and return a map with the sequance of the error messages for each variable. I've decided to use mutable collections for this because I think there is no other choise left:
val errors = collection.mutable.Map[String, ListBuffer[String]]()
//field1
val fieldToValidate1 = getData1()
if (fieldToValidate1 = "")
errors("fieldToValidate1") += "it must not be empty!"
if (validate2(fieldToValidate1))
errors("fieldToValidate1") += "validation2!"
if (validate3(fieldToValidate1))
errors("fieldToValidate1") += "validation3!"
//field2
val fieldToValidate2 = getData1()
//approximately the same steps
if (fieldToValidate2 = "")
errors("fieldToValidate2") += "it must not be empty!"
//.....
To my mind, it look kind of clumsy and there should other elegant solution. I'd also like to not use mutable collections if possible. Your ideas?
Instead of using mutable collections, you can define errors with var and update it in this way.
var errors = Map[String, List[String]]().withDefaultValue(Nil)
errors = errors updated ("fieldToValidate1", errors("fieldToValidate1") ++ List("it must not be empty!"))
errors = errors updated ("fieldToValidate1", errors("fieldToValidate1") ++ List("validation2"))
The code looks more tedious, but it gets out of mutable collections.
So what is a good type for your check? I was thinking about A => Option[String] if A is the type of your object under test. If your error messages do not depend on the value of the object under test, (A => Boolean, String) might be more convenient.
//for constructing checks from boolean test and an error message
def checkMsg[A](check: A => Boolean, msg: => String): A => Option[String] =
x => if(check(x)) Some(msg) else None
val checks = Seq[String => Option[String]](
checkMsg((_ == ""), "it must not be empty"),
//example of using the object under test in the error message
x => Some(x).filterNot(_ startsWith "ab").map(x => x + " does not begin with ab")
)
val objectUnderTest = "acvw"
val errors = checks.flatMap(c => c(objectUnderTest))
Error labels
As I just noted, you were asking for a map with a label for each check. In this case, you need to provide the check label, of course. Then the type of your check would be (String, A => Option[String]).
Although a [relatively] widespread way of doing-the-thing-right would be using scalaz's Validation (as #senia has shown), I think it is a little bit overwhelming approach (if you're bringing scalaz to your project you have to be a seasoned scala developer, otherwise it may bring you more harm than good).
Nice alternative could be using ScalaUtils which has Or and Every specifically made for this purpose, in fact if you're using ScalaTest you already have seen an example of them in use (it uses scalautils underneath). I shamefully copy-pasted example from their doc:
import org.scalautils._
def parseName(input: String): String Or One[ErrorMessage] = {
val trimmed = input.trim
if (!trimmed.isEmpty) Good(trimmed) else Bad(One(s""""${input}" is not a valid name"""))
}
def parseAge(input: String): Int Or One[ErrorMessage] = {
try {
val age = input.trim.toInt
if (age >= 0) Good(age) else Bad(One(s""""${age}" is not a valid age"""))
}
catch {
case _: NumberFormatException => Bad(One(s""""${input}" is not a valid integer"""))
}
}
import Accumulation._
def parsePerson(inputName: String, inputAge: String): Person Or Every[ErrorMessage] = {
val name = parseName(inputName)
val age = parseAge(inputAge)
withGood(name, age) { Person(_, _) }
}
parsePerson("Bridget Jones", "29")
// Result: Good(Person(Bridget Jones,29))
parsePerson("Bridget Jones", "")
// Result: Bad(One("" is not a valid integer))
parsePerson("Bridget Jones", "-29")
// Result: Bad(One("-29" is not a valid age))
parsePerson("", "")
// Result: Bad(Many("" is not a valid name, "" is not a valid integer))
Having said this, I don't think you can do any better than your current approach if you want to stick with core scala without any external dependencies.
In case you can use scalaz the best solution for aggregation errors is Validation:
def validate1(value: String) =
if (value == "") "it must not be empty!".failNel else value.success
def validate2(value: String) =
if (value.length > 10) "it must not be longer than 10!".failNel else value.success
def validate3(value: String) =
if (value == "error") "it must not be equal to 'error'!".failNel else value.success
def validateField(name: String, value: String): ValidationNel[(String, String), String] =
(
validate1(value) |#|
validate2(value) |#|
validate3(value)
).tupled >| value leftMap { _.map{ name -> _ } }
val result = (
validateField("fieldToValidate1", getData1()) |#|
validateField("fieldToValidate2", getData2())
).tupled
Then you could get optional errors Map like this:
val errors =
result.swap.toOption.map{
_.toList.groupBy(_._1).map{ case (k, v) => k -> v.map(_._2) }
}
// Some(Map(fieldToValidate2 -> List(it must not be equal to 'error'!), fieldToValidate1 -> List(it must not be empty!)))