krismath
krismath

Reputation: 1969

Scala Regex Partial Function with Regex defined in Partial Function

Question

From this answer to a related Scala Regex Partial Function question, I am able to define a partial function which matches a regex pattern using the following code:

val regex = "(\\d+),([A-z]+)".r
val input = List("1,a", "23,zZ", "1", "1ab", "")

scala> input collect { case regex(a, b) => (a, b) }
res2: List[(String, String)] = List((1,a), (23,zZ))

The first line defines the regex while the second line defines the input. Is it possible that I move the definition of the regex to be inside the partial function? I have tried doing:

scala> input collect { val regex = "(\\d+),([A-z]+)".r; case regex(a, b) => (a, b) }

But the code will not compile. Is it possible to define the regex to be matched inside the function (or at least scope enclosed)?

Context

The input is a list of strings. Each string would match a different regex and would be handled differently. For example, if the string is a number-alphabets pair, return the number; if the string is a date in ISO format, return the year; if the string is an uppercase string, return the last 4 characters.

Input: List("1,a", "23,zZ", "1990-10-21", "1ab", "", "ABCDEF")

Output: List("1", "23", "1990", "CDEF")

The number of cases and the cases themselves could change, so the code should be able to handle those changes.

Upvotes: 0

Views: 288

Answers (2)

hamednourhani
hamednourhani

Reputation: 106

import scala.util.matching.Regex

trait RegexRule {
  val rule: Regex

  def apply(): PartialFunction[String, String] = {
    case rule(a, _*) => a
  }
}

object RegexRule {

  def createMatcher(l: Seq[RegexRule]): PartialFunction[String, String] =
    l.map(_.apply()).reduce(_.orElse(_))
}

object DateRegexRule extends RegexRule {
  val rule: Regex = "(\\d{4})-(\\d{2})-(\\d{2})".r
}

object NumberRegexRule extends RegexRule {
  val rule: Regex = "(\\d+),([A-z]+)".r
}

object AlphabeticRegexRule extends RegexRule {
  val rule: Regex = "([A-Z]+)".r
  override def apply(): PartialFunction[String, String] =
    super.apply().andThen(_.takeRight(4))

}

object PartialFunctionWithRegex extends App {

  val input = List("1,a", "23,zZ", "1991-12-31", "1ab", "", "ABCEDT")

  val regexRules: Seq[RegexRule] =
    Seq(DateRegexRule, NumberRegexRule, AlphabeticRegexRule)

  val matchers: PartialFunction[String, String] =
    RegexRule.createMatcher(regexRules)

  val result = input.collect(matchers)
  println(result)
  //List(1, 23, 1991, CEDT)

}

just you need to implement new instance of RegexRule for each kind of regex.

Upvotes: 0

krismath
krismath

Reputation: 1969

I was able to define the regex in an enclosed scope that returns the partial function:

{ val regex = "(\\d+),([A-z]+)".r; { case regex(a, _*) => a } }

The partial function can be used as:

val input = List("1,a", "23,zZ", "1991-12-31", "1ab", "")
val matchers: List[PartialFunction[String, String]] = List(
  { val regex = "(\\d{4})-(\\d{2})-(\\d{2})".r; { case regex(a, _*) => a } },
  { val regex = "(\\d+),([A-z]+)".r; { case regex(a, _*) => a } },
  { val regex = "([A-Z]+)".r; { case regex(a) => a takeRight 4 } },
)

Upvotes: 1

Related Questions