pattern matching in String Scala - scala

I wrote the code above to define the type of String based on some rules.
def dataType (input:String) : String = input match {
case input if input.startsWith("Q") => "StringType";
case input if (input.startsWith("8") && !(input.contains("F"))) => "IntegerType"
case input if (input.startsWith("8") && (input.contains("F"))) => "FloatType"
case _ => "UnknowType";
}
This code works well , but I want to optimize it by avoiding the use of If satements. I want it to be based on pattern matching only without any use of if statements.
I tried to modify it this way , but it gives me bad results :
def dataType (input:String) : String = input match {
case "startsWith('Q')" => "StringType"
case "startsWith('8') && !(contains('F')))" => "IntegerType"
case "startsWith('8') && (contains('F')))" => "FloatType"
case _ => "UnknowType";
}
it always gives me the UnknownType result
Any help with this please
Best Regards

Since you are checking for the initial letter and boolean for containing F, you can create Tuple2[Char, Boolean] of those cases and use it in you match case as following
def dataType (input:String) : String = (input.charAt(0), input.contains('F')) match {
case ('8', true) => "FloatType"
case ('Q', _) => "StringType"
case ('8', false) => "IntegerType"
case _ => "UnknowType"
}
And you should be fine

Related

Scala: How to add match vals to a list val

I have a few vals that match for matching values
Here is an example:
val job_ = Try(jobId.toInt) match {
case Success(value) => jobs.findById(value).map(_.id)
.getOrElse( Left(WrongValue("jobId", s"$value is not a valid job id")))
case Failure(_) => jobs.findByName(jobId.toString).map(_.id)
.getOrElse( Left(WrongValue("jobId", s"'$jobId' is not a known job title.")))
}
// Here the value arrives as a string e.i "yes || no || true || or false" then converted to a boolean
val bool_ = bool.toLowerCase() match {
case "yes" => true
case "no" => false
case "true" => true
case "false" => false
case other => Left(Invalid("bool", s"wrong value received"))
}
Note: invalid case is case class Invalid(x: String, xx: String)
above i'm looking for a given job value and checking whether it exist in the db or not,
No I have a few of these and want to add to a list, here is my list val and flatten it:
val errors = List(..all my vals errors...).flatten // <--- my_list_val (how do I include val bool_ and val job_)
if (errors.isEmpty) { do stuff }
My result should contain errors from val bool_ and val job_
THANK!
You need to fix the types first. The type of bool_ is Any. Which does not give you something you can work with.
If you want to use Either, you need to use it everwhere.
Then, the easiest approach would be to use a for comprehension (I am assuming you're dealing with Either[F, T] here, where WrongValue and Invalid are both sub-classes of F and you're not really interested in the errors).
for {
foundJob <- job_
_ <- bool_
} yield {
// do stuff
}
Note, that in Scala >= 2.13 you can use toIntOption when converting the String to Int:
vaj job_: Either[F, T] = jobId.toIntOption match {
case Some(value) => ...
case _ => ...
}
Also, in case expressions, you can use alternatives when you have the same statement for several cases:
val bool_: Either[F, Boolean] = bool.toLowerCase() match {
case "yes" | "true" => Right(true)
case "no" | "false" => Right(false)
case other => Left(Invalid("bool", "wrong value received"))
}
So, according to your question, and your comments, these are the types you're dealing with.
type ID = Long //whatever id is
def WrongValue(x: String, xx: String) :String = "?-?-?"
case class Invalid(x: String, xx: String)
Now let's create a couple of error values.
val job_ :Either[String,ID] = Left(WrongValue("x","xx"))
val bool_ :Either[Invalid,Boolean] = Left(Invalid("x","xx"))
To combine and report them you might do something like this.
val errors :List[String] =
List(job_, bool_).flatMap(_.swap.toOption.map(_.toString))
println(errors.mkString(" & "))
//?-?-? & Invalid(x,xx)
After checking types as #cbley explained. You can just do a filter operation with pattern matching on your list:
val error = List(// your variables ).filter(_ match{
case Left(_) => true
case _ => false
})

Pattern matching a domain name

I don't use pattern matching as often as I should.
I am matching a domain name for the following:
1. If it starts with www., then remove that portion and return.
www.stackoverflow.com => "stackoverflow.com"
2. If it has either example.com or example.org, strip that out and return.
blog.example.com => "blog"
3. return request.domain
hello.world.com => "hello.world.com"
def filterDomain(request: RequestHeader): String = {
request.domain match {
case //?? case #1 => ?
case //?? case #2 => ?
case _ => request.domain
}
}
How do I reference the value (request.domain) inside the expression and see if it starts with "www." like:
if request.domain.startsWith("www.") request.domain.substring(4)
You can give the variable you pattern matching a name and Scala will infer its type, plus you can put an if statement in you case expression as follows
def filterDomain(request: RequestHeader): String = {
request.domain match {
case domain if domain.startsWith("www.") => domain.drop(4)
case domain if domain.contains("example.org") | domain.contains("example.com") => domain.takeWhile(_!='.')
case _ => request.domain
}
}
Note that the order of the case expressions matters.
When writing case clauses you can do something like:
case someVar if someVar.length < 2 => someVar.toLowerCase
This should make pretty clear how grabbing matched values works.
So in this case, you would need to write something like:
case d if d.startsWith("www.") => d.substring(4)
If you're dead set on using a regex rather than String methods such as startsWith and contains, you can do the following:
val wwwMatch = "(?:www\\.)(.*)".r
val exampleMatch = "(.*)(?:\\.example\\.(?:(?:com)|(?:org)))(.*)".r
def filterDomain(request: String): String = {
request.domain match {
case wwwMatch(d) => d
case exampleMatch(d1, d2) => d1 + d2
case _ => request.domain
}
}
Now, for maintainability's sake, I wouldn't go this way, because a month later, I will look at this and not remember what it's doing, but that's your call.
you don't need pattern matching for that:
request.domain
.stripPrefix("www.")
.stripSuffix(".example.org")
.stripSuffix(".example.com")

How to use strings as type without hardcoding them?

I have some code to convert json string to objects like following:
def apply(line: String): PairEvent = {
val (name, json) = line.span(_ != ' ')
name match {
case "OpenTabEvent" => Serialization.read[OpenTabEvent](json)
case "CloseTabEvent" => Serialization.read[CloseTabEvent](json)
case "ChangeContentEvent" => Serialization.read[ChangeContentEvent](json)
case "ChangeMasterRequest" => Serialization.read[ChangeMasterRequest](json)
case "CreateFileEvent" => Serialization.read[CreateFileEvent](json)
case "DeleteFileEvent" => Serialization.read[DeleteFileEvent](json)
case "CreateDirEvent" => Serialization.read[CreateDirEvent](json)
case "DeleteDirEvent" => Serialization.read[DeleteDirEvent](json)
case "WatchingFiles" => Serialization.read[WatchingFiles](json)
case _ =>
ServerLogger.info("!!!!!!!!!!!!!!!!!!!!! unknown line from server: " + line)
???
}
}
You can see I hardcoded some strings and use them as types to convert to objects.
Is there any way to do the same without hardcode them? I'm not sure if it's possible, even with macro.
You could use the class name:
case OpenTabEvent.getClass.getSimpleName => Serialization.read[OpenTabEvent](json)

Scala match case on regex directly

I am trying to do something like the following:
list.foreach {x =>
x match {
case """TEST: .*""" => println( "TEST" )
case """OXF.*""" => println("XXX")
case _ => println("NO MATCHING")
}
}
The idea is to use it like groovy switch case regex match. But I can't seem to get to to compile. Whats the right way to do it in scala?
You could either match on a precompiled regular expression (as in the first case below), or add an if
clause. Note that you typically don't want to recompile the same regular expression on each case evaluation, but rather have it on an object.
val list = List("Not a match", "TEST: yes", "OXFORD")
val testRegex = """TEST: .*""".r
list.foreach { x =>
x match {
case testRegex() => println( "TEST" )
case s if s.matches("""OXF.*""") => println("XXX")
case _ => println("NO MATCHING")
}
}
See more information here and some background here.
Starting Scala 2.13, it's possible to directly pattern match a String by unapplying a string interpolator:
// val examples = List("Not a match", "TEST: yes", "OXFORD")
examples.map {
case s"TEST: $x" => x
case s"OXF$x" => x
case _ => ""
}
// List[String] = List("", "yes", "ORD")

How to match a string on a prefix and get the rest?

I can write the code like this:
str match {
case s if s.startsWith("!!!") => s.stripPrefix("!!!")
case _ =>
}
But I want to know is there any better solutions. For example:
str match {
case "!!!" + rest => rest
case _ =>
}
val r = """^!!!(.*)""".r
val r(suffix) = "!!!rest of string"
So suffix will be populated with rest of string, or a scala.MatchError gets thrown.
A different variant would be:
val r = """^(!!!){0,1}(.*)""".r
val r(prefix,suffix) = ...
And prefix will either match the !!! or be null. e.g.
(prefix, suffix) match {
case(null, s) => "No prefix"
case _ => "Prefix"
}
The above is a little more complex than you might need, but it's worth looking at the power of Scala's regexp integration.
Starting Scala 2.13, it's now possible to pattern match a String by unapplying a string interpolator:
"!!!hello" match {
case s"!!!$rest" => rest
case _ => "oups"
}
// "hello"
If it's the sort of thing you do often, it's probably worth creating an extractor
object BangBangBangString{
def unapply(str:String):Option[String]= {
str match {
case s if s.startsWith("!!!") => Some(s.stripPrefix("!!!"))
case _ => None
}
}
}
Then you can use the extractor as follows
str match{
case BangBangBangString(rest) => println(rest)
case _ => println("Doesn't start with !!!")
}
or even
for(BangBangBangString(rest)<-myStringList){
println("rest")
}
Good question !
Even i was trying a lot to find out the answer.
Here is a good link where I found the answer
object _04MatchExpression_PatternGuards {
def main(args: Array[String]): Unit = {
val url: String = "Jan";
val monthType = url match {
case url if url.endsWith(".org") => "Educational Websites";
case url if url.endsWith(".com") => "Commercial Websites";
case url if url.endsWith(".co.in") => "Indian Websites"
case _ => "Unknow Input";
}
}
}