Better way to write cascading if statements in Scala? - scala

In JavaScript we can rewrite:
if (ua.isEmpty()) {
return false;
}
else if (ua.contains('curl')) {
return false;
}
into this for clear code:
switch(true) {
case ua.isEmpty():
return false;
case ua.contains('curl'):
return false;
}
Any suggestion we could do things like this in Scala?

If all you care about is these two conditions you can just have some thing like this
if(ua.isEmpty || ua.contains('curl')) false
if you want to have more cases you can do something like this
ua match{
case _ if(ua.isEmpty) => false
case _ if(ua.contains('curl') => false
case _ => //return whatever you want if none of the above is true
}
or with a traditional if else
if(ua.isEmpty)
false
else if(ua.contains('curl')
false
else
// return whatever you want
Notice that if you don't add the final else or the final case _=> then the return type will be Any and not Boolean

As addition to Dionysis answer:
You can also use the type of the object you want to check - to make it more readable.
In your case if ua is a List:
ua match{
case Nil => false
case l if l.contains('curl') => false
case _ => true
}
As you see I also made some other small adjustments:
parameters () in if are not needed
I prefer to name the matched value and use this name in the if

If you are using Scala, I recommend using Options with ua ua: Option[String].
val ua: Option[String] = // Some("String") or None
val result = ua match {
case Some(x: String) if x.contains("curl") => false
case Some(x) => // What you want
case None => false
case _ => // Error
}
If you want to use if you should use ua: String (not recommended).
val ua: String = // "String" or ""
val result = if (ua.contains('curl') || ua.isEmpty || ua != "") false else // What you want
You should not use val ua: String = null the answer is here

Related

Scala way of coding

I am moving from C / C++to Scala, and following is my code -
something match {
case one: {
if (some_list.nonEmpty) {
if (some_list size == 1 && some_list contains == something)
fill a form(use something in a certain way)
else if (some_list size == 1 && some_list contains == soemthing_else)
fill a form(use something_else in a certain way)
else {
if (some_var.nonEmpty) {
fill a form(use some_var)
} else {
fill a form(without using some_var)
}
}
} else {
if (another_var has certain value || another_var has certain value 2) {
fill a form(using another_var)
} else {
fill a form(without using another_var)
}
} //if (some_list.nonEmpty) ends
} // case ends
case two: one liner code
case _: one liner code
} //match ends
Looking
for guidance to write it in a nice scala way using its features and strengths.
Thank you
I am making a few assumptions to make this work:
trait cases
case object one extends cases
case object two extends cases
case object three extends cases
case object four extends cases
val someList: List[cases] = List(one, two)
val something: cases = one
val somethingElse: cases = two
val someVar: Option[String] = Option("someVar")
val someOtherVar: Option[String] = Option("someOtherVar")
val anotherVar: Option[String] = Option("anotherVar")
Following is a simplified version of your code using the above:
something match {
case `one` =>
someList match {
case head :: Nil if(head == something) => println("one")
case head :: Nil if(head == somethingElse) => println("two")
case head :: tail if(someVar.nonEmpty) => println("someVar")
case head :: tail if(someOtherVar.nonEmpty) => println("someOtherVar")
case head :: tail => println("not using someOtherVar")
case Nil if(anotherVar.nonEmpty) => println("anotherVar")
case Nil => println("not using anotherVar")
}
case `two` => println("two")
case _ => println("rest")
}

Using scala.Option functionally

I have an Option, say O, which can either be None or may have some value inside. If it has some value, that value may have a flag, say f. My requirement is that if O is None, then I create an object, say of type MyEntity,but if O has a value with flag as true, I return Nil else I create instance of MyEntity with different value. Java code can be almost as:
if(O.isEmpty) {
MyEntity("x")
} else {
if(O.f) {
Nil
} else {
MyEntity("y") // with different value
}
}
I want to do this in functional style using HoFs provided in scala.Option. What would be best possible way of doing it? I could this so far :
if(O.isEmpty){
MyEntity("x")
} else {
Option.unless(O.exists(_.f))(MyEntity("y"))
}
I misread your question the first go round, so here is attempt #2
This is a great case for pattern matching:
val maybeMyEntity: Option[MyEntity] = option match {
case Some(flagValue) if flagValue => None
// case Some(true) => None (More concise, but does not highlight a handy feature)
case Some(_) => Some(MyEntity("Y"))
case None => Some(MyEntity("X"))
}
Pattern matching is very powerful.
Alternatively, mapping is another option:
mapping of an Option will only occur if its value is not empty, and flatMapping will remove the layer of Option added, from Option[Option[MyEntity]] to Option[MyEntity]
val result: Option[MyEntity] = if (option.isEmpty) {
Some(Entity("X"))
} else {
option.flatMap { flagValue =>
if (flagValue) {
None
} else {
Some(MyEntity("Y"))
}
}
}
As mentioned in the comments, Nil type is a List, and your expression should always return the same type (should really not be Any).
One possibility is to define a "sentinel" value for MyEntity, e.g.:
object MyEntity {
val Nil = MyEntity("")
}
Now you can write:
val result = O.fold(MyEntity("x")) {
case true => MyEntity.Nil
case false => MyEntity("y")
}
case class AnOption(var flag: Boolean = true)
case class MyEntities(name: String)
val myOptionEntityCheck = (someOption: Option[AnOption]) => {
someOption match {
case None => Some(MyEntities("X"))
case Some(aValue: AnOption) if aValue.flag => None
// OR case Some(AnOption(true)) => None
case Some(_) => Some(MyEntities("y"))
}
}

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
})

Scala - How to return Option[String] instead of Any with pattern matching?

I wanted to get Sale objects from HBase concatenated with their HBase ids (a string representation of ImmutableBytesWritable) as Option[String].
First I've implemented processSales method so that it just returned all sales + hBase ids as shown below:
private def processSales (result: Result, hBaseId: String): Option[String] = {
val triedSale: Try[Sale] = myThriftCodec.invert(result.getValue("binary", "object"))
triedSale match {
case Success(sale) => Some(hBaseId + sale)
case _ => None
}
}
Now I want to return only those concatenated hBaseIds + Sales where Sales have metric_1 == null
So I tried the following:
private def processSales (result: Result, hBaseId: String): Any = {
val triedSale: Try[Sale] = myThriftCodec.invert(result.getValue("binary", "object"))
triedSale match {
case Success(sale) => Some(hBaseId + sale)
case _ => None
}
triedSale match {
case someSale => if (someSale.get.metric_1 = null) someSale.map(sale => hBaseId + sale)
}
}
But it seems that I'm missing something here and the method returns Any even if I wrap this like this Option(hBaseId + sale).
What should I fix in my logic in order to return Option[String] with sales having metric_1 == null ?
UPD: Downvoting without pointing out the problems with my question doesn't make sense. It just totally demotivates seeking new knowledge.
You are missing the second option of the match case in your else, so it's returning Unit when the metric is not null, so Unit in one case, and Option(String) in another, the compiler guess that you want Any as return type
What do you want to return when the metric_1 is not null? In this example you return the exact same input:
triedSale match {
case someSale => if (someSale.get.metric_1 = null) someSale.map(s => hBaseId + s) else someSale
}
Or in a more elegant way you can do:
triedSale match {
case Success(metric_1) if metric_1 = null => Some(hBaseId + metric_1)
case Success(metric_1) if metric_1 != null => triedSale
case _ => None
}
EDIT
As per the comments, you only want to return something when the metric_1 is null so here is the best solution as for my understanding:
Also why are you pattern matching the same variable twice?
triedSale match {
case someSale => if (someSale.get.metric_1 = null) someSale.map(s => hBaseId + s) else None
}
Or something like this:
triedSale match {
case Success(metric_1) if metric_1 = null => Some(hBaseId + metric_1)
case _ => None
}
Isn't just as simple as below?
myThriftCodec.invert(result.getValue("binary", "object"))
.toOption
.filter(_.metric_1 == null)
.map(hBaseId+_)

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