0

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.

krismath
  • 1,879
  • 2
  • 23
  • 41
  • 1
    Please explain why do you need that. Regex is immutable and stateless so it is safe to create it once and use in partial function. – fenixil Sep 06 '19 at 03:16
  • I want to create multiple regexes to be matched against. Naming the regexes `regex1` would not be good, and I don't think that `regexes(0)` would be any better. If there is a way to enclose the regex in a scope, I can name the variable as `regex` and use it to match patterns. – krismath Sep 06 '19 at 03:19
  • I meant that I have multiple regex patterns to match each string in `input` against. So there will be `regex1`, `regex2`, `regex3`, and so on. I think it would be cleaner to not name them `regexN` but to instead keep the regex isolated in its own scope, named as `regex`. – krismath Sep 06 '19 at 03:28
  • 1
    Can you please update your question with these details. It looks like xy problem, when you think that enclosure will resolve the problem but in fact you are trying to do something absolutely different. Do you want to select all values that match any regex (in array)? – fenixil Sep 06 '19 at 03:29
  • I am trying to write multiple partial functions. Each partial function will handle a single non-overlapping regex pattern in a completely different way. That's why I think that partial functions should be used. – krismath Sep 06 '19 at 03:53

2 Answers2

1

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 } },
)
krismath
  • 1,879
  • 2
  • 23
  • 41
0
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.

hamednourhani
  • 106
  • 1
  • 6