WelcomeTo
WelcomeTo

Reputation: 20571

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

Upvotes: 8

Views: 10617

Answers (4)

Silvio Bierman
Silvio Bierman

Reputation: 713

How about:

myMap.get(x)
    .zip(myMap.get(y))
    .headOption.map(x => x._1 + x._2)
    .orElse(myMap.get(x + y)
    .filter(__ % 2 == 0))

Upvotes: 3

michael_s
michael_s

Reputation: 2575

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

Upvotes: 3

ziggystar
ziggystar

Reputation: 28670

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

Upvotes: 22

Shadowlands
Shadowlands

Reputation: 15074

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
  }

Upvotes: 2

Related Questions