Reputation: 7764
I have below function which returns a flag based on some conditions.
Trying to see if there is a better Scala way to achieve the same?
Basically based on the status of the person there are different rules to be applied finally to derive on the pass_ind flag.
If person status is Student or Unemployment there are some rules on income and if the person status is not in Student or Unemployment there are different rules to be applied on score & income
def IncomeScreenStatus(status_cd: Option[String],
score: Option[Int],
income: Option[Double]) : String = {
var pass_ind : String = "F"
if (score.isDefined && income.isDefined && status_cd.isDefined) {
val score : Int = score.get
val income : Double = income.get
val status_cd : String = status_cd.get
if (status_cd == "STUDENT" || status_cd == "UNEMPLOYMENT") {
pass_ind = (status_cd, income) match {
case ("UNEMPLOYMENT", income) if income <= 7000 => "P"
case ("STUDENT", income) if income <= 18000 => "P"
case _ => "F"
}
}
else {
pass_ind = (income, score) match {
case (income, score) if (score < 100 || score > 150) && income <= 8500 => "P"
case (income, score) if (score <= 167 && score >= 100) && income <= 10500 => "P"
case _ => "F"
}
}
}
pass_ind
}
Upvotes: 0
Views: 694
Reputation: 990
As your example requires each field to be defined, you can zip the Option
s together. I also like to use extractors in place of explicit guards on my case statements.
If your status is a finite set, I would also recommend using a sealed trait
to represent it.
object Example {
sealed trait Status
case object Student extends Status
case object Unemployed extends Status
case object Other extends Status
object PoorStudent {
def unapply(income: Int) = if (income <= 18000) Some(income) else None
}
object PoorUnemployed {
def unapply(income: Int) = if (income <= 7000) Some(income) else None
}
object IncomeScoreRange {
def unapply(score: Int, income: Double) =
if (((score < 100 || score > 150) && income <= 8500) || (income <= 10500 && (100 to 167 contains score)))
Some((score, income))
else
None
}
def compute(status: Option[Status], score: Option[Int], income: Option[Double]): String = {
(status, score, income).zipped
.collect {
case (Student, _, PoorStudent(_)) => "P"
case (Unemployed, _, PoorUnemployed(_)) => "P"
case (Other, IncomeScoreRange(_, _)) => "P"
}
.headOption
.getOrElse("F")
}
}
Upvotes: 0
Reputation: 1085
In functional programming, it is better to have small functions instead of large one. I would prefer doing something as below:
def screenStudent(income:Double):String = {
if (income <= 18000) "P" else "F"
}
def screenUnemployment(income:Double):String = {
if (income <= 7000) "P" else "F"
}
def screenOthers(income:Double, score:Int):String = {
(income, score) match {
case (income, score) if (score < 100 || score > 150) && income <= 8500 => "P"
case (income, score) if (score <= 167 && score >= 100) && income <= 10500 => "P"
case _ => "F"
}
}
def incomeScreenStatus(
status_cd: Option[String],
score: Option[Int],
income: Option[Double]
) : String =
(status_cd , score, income) match {
case (Some("STUDENT"), Some(_), Some(i)) => screenStudent(i)
case (Some("UNEMPLOYMENT"), Some(_), Some(i)) => screenUnemployment(i)
case (Some(_), Some(s), Some(i)) => screenOthers(i, s)
case _ => "F"
}
Upvotes: 0
Reputation: 51271
Here's a first crack at it.
def IncomeScreenStatus(status_cd: Option[String]
,score : Option[Int]
,income : Option[Double]) : String = {
for {
stat <- status_cd
scr <- score
incm <- income
} yield stat match {
case "UNEMPLOYMENT" => if (incm <= 7000) "P" else "F"
case "STUDENT" => if (incm <= 18000) "P" else "F"
case _ => if ((scr < 100 || scr > 150) && incm <= 8500 ||
(scr <= 167 && scr >= 100) && incm <= 10500) "P" else "F"
}
}.getOrElse("F")
Creating local variables with the same name as the passed in parameters really confuses the compiler.
Upvotes: 1
Reputation: 13985
First of all, I have few comments about recommended code-style adopted by Scala,
camelCase
incomeScreenStatus
instead of IncomeScreenStatus
.statusCd
instead of status_cd
passInd
instead of pass_ind
Now,
def incomeScreenStatus(statusCd: Option[String],
score: Option[Int],
income: Option[Double]) : String = {
(score, income, statusCd) match {
case (Some(scoreV), Some(incomeV), Some(statusCdV)) => {
(statusCdV, incomeV, scoreV) match {
case ("UNEMPLOYMENT", _, _) if incomeV <= 7000 => "p"
case ("STUDENT", _, _) if incomeV <= 18000 => "p"
case _ if (scoreV < 100 || scoreV > 150) && incomeV <= 8500 => "P"
case _ if (scoreV <= 167 && scoreV >= 100) && incomeV <= 10500 => "P"
case _ => "F"
}
}
case _ => "F"
}
}
Upvotes: 1
Reputation: 14073
Maybe something like this? (I haven't tried it, no guarantess it's correct.)
def IncomeScreenStatus( mb_status_cd : Option[String], mb_score : Option[Int], mb_income : Option[Double]) : String = {
def nsu( score : Int, income : Double ) = { // neither student nor unemployed
if ( (score < 100 || score > 150) && income <= 8500 ) "P"
else if ( (score <= 167 && score >= 100) && income <= 10500 ) "P"
else "F"
}
( mb_status_cd, mb_score, mb_income ) match {
case ( Some( "STUDENT" ), Some( score ), Some( income ) ) if ( income <= 18000 ) => "P"
case ( Some( "UNEMPLOYMENT" ), Some( score ), Some( income ) ) if ( income <= 7000 ) => "P"
case ( Some( "STUDENT" | "UNEMPLOYMENT" ), Some( _ ), Some( _ ) ) => "F"
case ( Some( _ ), Some( score ), Some( income ) ) => nsu( score, income )
case _ => "F"
}
}
Upvotes: 1