Scala way of coding - scala

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

Related

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

Efficient way to compare result and send a custom object in scala

val date2 = Option(LocalDate.parse("2017-02-01"))
case class dummy(val prop:Seq[Test])
case class Test(val s :String,val dt:String)
case class Result(val s :String)
def myFunc:Result = {
val s = "11,22,33"
val t = Test(s,"2017-02-06")
val list = dummy(Seq(t))
val code = Option("22")
val result = code.exists(p => {
list.prop.exists(d => d.s.split(",").contains(p) && (LocalDate.parse(d.dt).compareTo(date2.get)>=0))
})
if (result) {
Result("found")
} else {
Result("Not Found")
}
}
The code determines the result based on condition.
Is there a efficient way to achieve the above in scala using map and avoiding date2.get
You should check pattern matching, as far as i can see, you have several cases:
- Code
- list
- date2
One way to avoid date2.get is this one belows:
(code, list, date2) match {
case (Some(p), dummy(l), Some(d2)) if l.exists(d => d.s.split(",").contains(p) && (LocalDate.parse(d.dt).compareTo(d2) >= 0)) => Result("found")
case (_, _, _) => Result("Not Found")
}
Also i don't know why you want to use map. It seems to me that this is not the proper tool for this job

condition matching in an array with case class in scala

I have a task need to find a particular string in an array:
1. if found, return its value;
2. if not found, return -1.
I wrote an "idea" code, but I don't know how to finish it correctly.
case class person(name:String, value: Int)
personList[Array[person]]
val result = personList match {
case x if x.name == "john" => x.value
case _ => -1 }
the complier reports errors at "case x if x.name"
Would this work for you?
persons.find(_.name == "john").fold(-1)(_.value)
Note: I've left the creation and/or population of the persons array up to you.
val result = personList.find(_.name=="john") match {
case some(x) => x.value
case None => -1 }

Cleanest way in Scala to avoid nested ifs when transforming collections and checking for error conditions in each step

I have some code for validating ip addresses that looks like the following:
sealed abstract class Result
case object Valid extends Result
case class Malformatted(val invalid: Iterable[IpConfig]) extends Result
case class Duplicates(val dups: Iterable[Inet4Address]) extends Result
case class Unavailable(val taken: Iterable[Inet4Address]) extends Result
def result(ipConfigs: Iterable[IpConfig]): Result = {
val invalidIpConfigs: Iterable[IpConfig] =
ipConfigs.filterNot(ipConfig => {
(isValidIpv4(ipConfig.address)
&& isValidIpv4(ipConfig.gateway))
})
if (!invalidIpConfigs.isEmpty) {
Malformatted(invalidIpConfigs)
} else {
val ipv4it: Iterable[Inet4Address] = ipConfigs.map { ipConfig =>
InetAddress.getByName(ipConfig.address).asInstanceOf[Inet4Address]
}
val dups = ipv4it.groupBy(identity).filter(_._2.size != 1).keys
if (!dups.isEmpty) {
Duplicates(dups)
} else {
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
val taken: Iterable[Inet4Address] = ipAvailability.filter(!_._2).keys
if (!taken.isEmpty) {
Unavailable(taken)
} else {
Valid
}
}
}
}
I don't like the nested ifs because it makes the code less readable. Is there a nice way to linearize this code? In java, I might use return statements, but this is discouraged in scala.
I personally advocate using a match everywhere you can, as it in my opinion usually makes code very readable
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil =>
val ipv4it = ipConfigs.map { ipc =>
InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address]
}
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil =>
val taken = ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys
if (taken.nonEmpty) Unavailable(taken) else Valid
case dups => Duplicates(dups)
}
case invalid => Malformatted(invalid)
}
Note that I've chosen to match on the else part first, since you generally go from specific to generic in matches, since Nil is a subclass of Iterable I put that as the first case, eliminating the need for an i if i.nonEmpty in the other case, since it would be a given if it didn't match Nil
Also a thing to note here, all your vals don't need the type explicitly defined, it significantly declutters the code if you write something like
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
as simply
val ipAvailability = ipv4it.map(ip => (ip, isIpAvailable(ip)))
I've also taken the liberty of removing many one-off variables I didn't find remotely necessary, as all they did was add more lines to the code
A thing to note here about using match over nested ifs, is that is that it's easier to add a new case than it is to add a new else if 99% of the time, thereby making it more modular, and modularity is always a good thing.
Alternatively, as suggested by Nathaniel Ford, you can break it up into several smaller methods, in which case the above code would look like so:
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil => wellFormatted(ipConfigs)
case i => Malformatted(i)
}
def wellFormatted(ipConfigs: Iterable[IpConfig]): Result = {
val ipv4it = ipConfigs.map(ipc => InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address])
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil => noDuplicates(ipv4it)
case dups => Duplicates(dups)
}
}
def noDuplicates(ipv4it: Iterable[IpConfig]): Result =
ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys match {
case Nil => Valid
case taken => Unavailable(taken)
}
This has the benefit of splitting it up into smaller more manageable chunks, while keeping to the FP ideal of having functions that only do one thing, but do that one thing well, rather than having god-methods that do everything.
Which style you prefer, of course is up to you.
This has some time now but I will add my 2 cents. The proper way to handle this is with Either. You can create a method like:
def checkErrors[T](errorList: Iterable[T], onError: Result) : Either[Result, Unit] = if(errorList.isEmpty) Right() else Left(onError)
so you can use for comprehension syntax
val invalidIpConfigs = getFormatErrors(ipConfigs)
val result = for {
_ <- checkErrors(invalidIpConfigs, Malformatted(invalidIpConfigs))
dups = getDuplicates(ipConfigs)
_ <- checkErrors(dups, Duplicates(dups))
taken = getAvailability(ipConfigs)
_ <- checkErrors(taken, Unavailable(taken))
} yield Valid
If you don't want to return an Either use
result.fold(l => l, r => r)
In case of the check methods uses Futures (could be the case for getAvailability, for example), you can use cats library to be able of use it in a clean way: https://typelevel.org/cats/datatypes/eithert.html
I think it's pretty readable and I wouldn't try to improve it from there, except that !isEmpty equals to nonEmpty.

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