Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace if-else with functional code

I know that in functional style all if-else blocks replaced by pattern matching. But how I can handle Maps with pattern matching in Scala? For example how I can rewrite this code in more functional style?

  val myMap= getMap()
  if(myMap.contains(x) && myMap.contains(y)) Some(myMap(x) + myMap(y))
    else if(myMap.contains(x + y)){
      val z = myMap(x + y)
      if (z % 2 == 0) Some(z)
      else None
    }
    else None
like image 594
WelcomeTo Avatar asked Aug 19 '13 10:08

WelcomeTo


4 Answers

Using if-else is totally acceptable for functional programming, because if-else in Scala is merely an expression. The reasons to decide between if-else and pattern-matching should be focused on improving readability, mainly.

Here's my try at rewriting your code. I'm actually not using pattern matching here, but a for-comprehension to sum the values.

def sumOfValues = for{
  mx <- myMap.get(x)
  my <- myMap.get(y)
} yield mx + my

def valueOfSumIfEven = myMap.get(x+y).filter(_ % 2 == 0) 

sumOfValues orElse valueOfSumIfEven
like image 182
ziggystar Avatar answered Dec 03 '22 02:12

ziggystar


To answer your question directly - you can filter the cases with an additional if:

getMap match {
  case myMap if (myMap.contains(x) && myMap.contains(y)) =>
     Some(myMap(x) + myMap(y))
  case myMap if (myMap.contains(x+y)) =>
    myMap(x+y) match {
      case z if (z % 2 == 0) => Some(z)
      case _                 => None
    }
  case _ => None
}

([edit: there actually is] Although there is are "else-if's" in Scala, ) this is actually a way of doing if-else-if-else (looking at the produced class-files this is what it actually does whereas if-else is equivalent to the ternary ?: in java, returning Unit implicitly when the final else is missing).

like image 32
michael_s Avatar answered Dec 03 '22 01:12

michael_s


How about:

myMap.get(x)
    .zip(myMap.get(y))
    .headOption.map(x => x._1 + x._2)
    .orElse(myMap.get(x + y)
    .filter(__ % 2 == 0))
like image 34
Silvio Bierman Avatar answered Dec 03 '22 00:12

Silvio Bierman


Well, you could write your test as follows:

myMap.get(x).flatMap(xVal => myMap.get(y).map(_ + xVal))
            .orElse(myMap.get(x+y).filter(_ % 2 == 0))

But what you have already may just be clearer to anyone trying to understand the intent. Note that the first line (from flatMap to the end) can also be written as a for-comprehension, as shown in @ziggystar's answer).

Maybe the modulo test part could be rewritten as a match, if that feels cleaner:

if(myMap.contains(x) && myMap.contains(y))
    Some(myMap(x) + myMap(y))
else myMap.get(x + y) match {
    case Some(z) if (z % 2 == 0) => Some(z)
    case _ => None
  }
like image 21
Shadowlands Avatar answered Dec 03 '22 01:12

Shadowlands