debduttoc
debduttoc

Reputation: 314

Simplify Massive Match Case - Scala

So in one of the places we have this huge variable match case statement.

Containing almost 150 distinct case statements

Looks horrible.

I want to break it up into smaller functions, I can group the matches into say 10 pieces and then the number case statements come down to around 15. Which is fine.

It currently looks like this

massiveCaseVariable match {
  case "one" => 1
  case "two" => 2
  case "three" => 3
  case "four" => 4
  case "five" => 5
  case "six" => 6
}

But I don't want to do it like this

massiveCaseVariable match {
  case "one" || "two" || "three" => firstCategory(massiveCaseVariable)
  case "four" || "five" || "six" => secondCategory(massiveCaseVariable)
}

def firstCategory(caseVariable: String): Int =
  caseVariable match {
    case "one"   => 1
    case "two"   => 2
    case "three" => 3
  }

def secondCategory(caseVariable: String): Int =
  caseVariable match {
    case "four" => 4
    case "five" => 5
    case "six"  => 6
  }

It's too repetitive. Is there a better more concise way to do this?

I'm on Scala 2.11

PS: The examples are for illustration purposes only. I'm definitely not trying to match Strings to Integers

Upvotes: 3

Views: 1045

Answers (4)

michaJlS
michaJlS

Reputation: 2510

Don't use pattern matching then, or at least not by itself.

trait Handler {
def canHandle(variable: String): Boolean
def handle(variable: String): Int
}
class HandlerCategory1 extends Handler {/*...*/}
class HandlerCategory2 extends Handler {/*...*/}
// ...

val variable: String = ???
val handlers = List(new HandlerCategory1(), new HandlerCategory2())

handlers.find(_.canHandle(variable)).map(_.handle(variable))

You can put your condition from pattern matching into specific canHandle() methods.

Upvotes: 1

Dmytro Mitin
Dmytro Mitin

Reputation: 51693

You can try to use extractor objects hiding some logic in unapply methods. It's hard to answer better without your actual code. Can you share couple of your actual cases describing their "repetitive" nature?

https://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html

  trait Hierarchy

  class Case1(val s: String) extends Hierarchy

  object Case1 {
    def unapply(arg: Case1): Boolean = arg.s == "one" || arg.s == "two"
  }

  class Case2(val s: String) extends Hierarchy

  object Case2 {
    def unapply(arg: Case2): Boolean = arg.s == "three" || arg.s == "four" || arg.s == "five"
  }

  class Case3(val s: String) extends Hierarchy

  object Case3 {
    def unapply(arg: Case3): Option[String] = if (arg.s == "six" || arg.s == "seven") Some(arg.s) else None
  }

  class Case4(val s: String) extends Hierarchy

  object Case4 {
    // some other logic
    def unapply(arg: Case4): Option[(String, Int)] = if (arg.s == "eight") Some(arg.s, 8) 
                                                     else if (arg.s == "nine") Some(arg.s, 9) 
                                                     else None
  }

  val massiveCaseVariable: Hierarchy = ???

  massiveCaseVariable match {
    case Case1() => ???
    case Case2() => ???
    case Case3(s) => ???
    case Case4(s, n) => ???
  }

Upvotes: 1

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27595

If you want to simply combine you matches then you can notice that these are actually partial functions (because matching can fail):

val firstCategory: PartialFunction[String, Int] = {
    case "one"   => 1
    case "two"   => 2
    case "three" => 3
  }

val secondCategory: PartialFunction[String, Int] = {
    case "four" => 4
    case "five" => 5
    case "six"  => 6
  }

which can be combined:

val all = firstCategory orElse secondCategory
all("one")

Interestingly many collections are partial functions e.g. Map, so:

val firstCategory = Map[String, Int](
    "one"   -> 1,
    "two"   -> 2,
    "three" -> 3
  )

val secondCategory = Map[String, Int](
    "four" -> 4,
    "five" -> 5,
    "six"  -> 6
  )

val all = firstCategory ++ secondCategory
all("one")

should work the same way in this example.

Upvotes: 10

talex
talex

Reputation: 20544

You can organize it in following way

  val all = firstCategory() ++ secondCategory()

  all("some").apply()

  def firstCategory() : Map[String, () => ()] = {
    Map(
      "first" -> (() => {}),
      "second" -> (() => {})
    )
  }
  def secondCategory(): Map[String, () => ()] = {
    Map(
      "third" -> (() => {
        print("some code")
      }),
      "forth" -> (() => {
        print("some other code")
      })
    )
  }

Upvotes: 0

Related Questions