How to use strings as type without hardcoding them? - scala

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)

Related

Pattern match with variable parameters

Please suggest best was to implement the below code:
Requirement: pass single string or no parameters
object Twofer {
def twofer(name: String*): String = name match {
case Seq(nm) => s"One for $nm, one for me."
case List() => "One for you, one for me."
}
}
Maybe something similar to the following:
def twofer(names: String*): String = List(names: _*) match {
case Nil => "No names!"
case n :: Nil => s"Single name $n"
case ls => s"Multiple names $ls"
}

pattern matching in String 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

How to use a Result[String] in Scala match expression

In the following code the first expression returns a Result[String] which contains one of the strings "medical", "dental" or "pharmacy" inside of a Result. I can add .toOption.get to the end of the val statement to get the String, but is there a better way to use the Result? Without the .toOption.get, the code will not compile.
val service = element("h2").containingAnywhere("claim details").fullText()
service match {
case "medical" => extractMedicalClaim
case "dental" => extractDentalClaim
case "pharmacy" => extractPharmacyClaim
}
Hard to say without knowing what Result is. If it's a case class, with the target String as part of its constructor, then you could pattern match directly.
Something like this.
service match {
case Result("medical") => extractMedicalClaim
case Result("dental") => extractDentalClaim
case Result("pharmacy") => extractPharmacyClaim
case _ => // default result
}
If the Result class doesn't have an extractor (the upapply() method) you might be able to add one just for this purpose.
I'm assuming this Result[T] class has a toOption method which returns an Option[T] - if that's the case, you can call toOption and match on that option:
val service = element("h2").containingAnywhere("claim details").fullText().toOption
service match {
case Some("medical") => extractMedicalClaim
case Some("dental") => extractDentalClaim
case Some("pharmacy") => extractPharmacyClaim
case None => // handle the case where the result was empty
}

Scala - dynamically choose function

I'm trying to dynamically choose a function, as follows:
val yld: (Nothing => Object) = {
column.getType match {
case PrimitiveType.PrimitiveTypeName.BINARY => new String(creader.getBinary.getBytes)
case PrimitiveType.PrimitiveTypeName.BOOLEAN => creader.getBoolean
case PrimitiveType.PrimitiveTypeName.DOUBLE => creader.getDouble
case PrimitiveType.PrimitiveTypeName.FLOAT => creader.getFloat
case PrimitiveType.PrimitiveTypeName.INT32 => creader.getInteger
case PrimitiveType.PrimitiveTypeName.INT64 => creader.getLong
case PrimitiveType.PrimitiveTypeName.INT96 => new String(creader.getBinary.getBytes)
case PrimitiveType.PrimitiveTypeName.FIXED_LEN_BYTE_ARRAY => new String(creader.getBinary.getBytes)
}
}
Question: However, the inner functions (e.g. creader.getBoolean, etc.) are (obviously) executed at the time of the match. How do I capture those functions as objects to execute at a later time rather than having them execute when yld is assigned?
Context:
This is so that in a later for loop, I don't have to match again:
for (i <- 0L to 900000) {
println(yld)
}
Because my object creader is an iterator on column-stored data, being able to make the type decision only one time should be more efficient than having to make it every time, i.e.
for (i <- 0L to 900000) {
column.getType match {
case PrimitiveType.PrimitiveTypeName.BINARY => println(new String(creader.getBinary.getBytes))
case PrimitiveType.PrimitiveTypeName.BOOLEAN => println(creader.getBoolean)
case PrimitiveType.PrimitiveTypeName.DOUBLE => println(creader.getDouble)
case PrimitiveType.PrimitiveTypeName.FLOAT => println(creader.getFloat)
case PrimitiveType.PrimitiveTypeName.INT32 => println(creader.getInteger)
case PrimitiveType.PrimitiveTypeName.INT64 => println(creader.getLong)
case PrimitiveType.PrimitiveTypeName.INT96 => println(new String(creader.getBinary.getBytes))
case PrimitiveType.PrimitiveTypeName.FIXED_LEN_BYTE_ARRAY => println(new String(creader.getBinary.getBytes))
}
}
If you have a method def foo = 42 you can convert it to a function and assign it to a val by val func = foo _.

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";
}
}
}