Scala : how to expose regex variables bound within a case statement - regex

Scala regex works great under either of two conditions:
unconditionally executed code:
e.g.
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
val anotherregx = """([\w]+)\t([/\w+=\-!%# ]+)""".r
val lineregx(category, aaUrl, title)
or if inside a case statement we consume the expressions (and don't need them again..)
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
line match {
case lineregx(category, aaUrl, title) => // do something with category, aaUrl and title in here!
case anotherRegx(category, aaUrl) => // do something different with category, aaUrl and title in here!
case _ => { println("did not match line %s".format(line)); return 0 }
}
But what about if i need to 'surface' the matches to variables outside of the case statement? Specifically the var's shown below,
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
var category = "" ; var aaUrl = "";var title = ""
line match {
case lineregx(category, aaUrl, title) => val lineregx(category, aaUrl, title) = line
case anotherRegx(category, aaUrl) => val lineregx(category, aaUrl) = line
case _ => { println("did not match line %s".format(line)); return 0 }
}
// Do something with category, aaUrl, title HERE after the case statement.
Problem is , the syntax for applying the lineregx/anotherregex makes those variables local to the case statement only.

Roughly,
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
val (category, aaUrl, title) = line match {
case lineregx(category, aaUrl, title) => (category, aaUrl, title)
case anotherRegx(category, aaUrl) => (category, aaUrl, ???)
case _ => { println("did not match line %s".format(line)); return 0 }
}
// Do something with category, aaUrl, title HERE after the case statement.
But that code is quite disorganized. For one thing, there's the question of the value of title for the second case. For another, there's the early return. Instead, the code would probably be best organized like this:
// method declaration
// ...
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
line match {
case lineregx(category, aaUrl, title) => f(category, aaUrl, title)
case anotherRegx(category, aaUrl) => f(category, aaUrl, ???)
case _ =>
println("did not match line %s".format(line))
0
}
} // end of method
def f(category: String, aaUrl: String, title: String): Int = {
// Do something with category, aaUrl, title HERE
}

You could use Option:
val lineregx = """([\w]+)\t([/\w.+-]+)\t([/\w+=\-!%# ]+)""".r
val (maybeCat, maybeUrl, maybeTitle) =
line match {
case lineregx(category, aaUrl, title) => (Some(category), Some(aaUrl), Some(title))
case anotherRegx(category, aaUrl) => (Some(category), Some(aaUrl), None)
case _ =>
println("did not match line %s".format(line))
(None, None, None)
}
var category = maybeCat getOrElse ""
var aaUrl = maybeURL getOrElse ""
var title = maybeTitle getOrElse ""
Slightly more verbose, but this way you can get the variables in the same scope.

Related

How to get integer with regular expression in kotlin?

ViewModel
fun changeQty(textField: TextFieldValue) {
val temp1 = textField.text
Timber.d("textField: $temp1")
val temp2 = temp1.replace("[^\\d]".toRegex(), "")
Timber.d("temp2: $temp2")
_qty.value = textField.copy(temp2)
}
TextField
OutlinedTextField(
modifier = Modifier
.focusRequester(focusRequester = focusRequester)
.onFocusChanged {
if (it.isFocused) {
keyboardController?.show()
}
},
value = qty.copy(
text = qty.text.trim()
),
onValueChange = changeQty,
label = { Text(text = qtyHint) },
singleLine = true,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
save()
onDismiss()
}
)
)
Set KeyboardType.Number, it display 1,2,3,4,5,6,7,8,9 and , . - space.
I just want to get integer like -10 or 10 or 0.
But I type the , or . or -(not the front sign), it show as it is.
ex)
typing = -10---------
hope = -10
display = -10---------
I put regular expression in
val temp2 = temp1.replace("[^\\d]".toRegex(), "")
But, it doesn't seem to work.
How I can get only integer(also negative integer)?
Use this regex (?<=(\d|-))(\D+) to replace all non digit characters, except first -.
fun getIntegersFromString(input: String): String {
val pattern = Regex("(?<=(\\d|-))(\\D+)")
val formatted = pattern.replace(input, "")
return formatted
}
Check it here

Parsing case statements in scala

Parsing case statements in scala
CASE WHEN col1 <> 0 AND col2 <> 0 THEN 'COL1 & COL2 IS NOT ZERO' ELSE 'COL1 & COL2 IS ZERO'
challenge here is to give all the scenarios where case statement can come for e.g. it can come inside a function. Also case statements/functions etc. can come inside another case statements which has to be handled.
This problem can be solved with scala parser combinator
first define the classes needed to map experssions
sealed trait Exp {
def asStr: String
override def toString: String = asStr
}
case class OperationExp(a: Exp, op: String, b: Exp, c: Option[String]) extends Exp { override def asStr = s"$a $op $b ${c.getOrElse("")}" }
case class CaseConditions(conditionValue: List[(String, String)] , elseValue: String, asAlias: Option[Exp]) extends Exp {
override def asStr = "CASE " + conditionValue.map(c => s"WHEN ${c._1} THEN ${c._2}").mkString(" ") + s" ELSE ${elseValue} END ${asAlias.getOrElse("")}"
}
now the solution
case class OperationExp(a: Exp, op: String, b: Exp, c: Option[String]) extends Exp { override def asStr = s"$a $op $b ${c.getOrElse("")}" }
case class CaseConditions(conditionValue: List[(String, String)] , elseValue: String, asAlias: Option[Exp]) extends Exp {
override def asStr = "CASE " + conditionValue.map(c => s"WHEN ${c._1} THEN ${c._2}").mkString(" ") + s" ELSE ${elseValue} END ${asAlias.getOrElse("")}"
}
val identifiers: Parser[String] = "[a-zA-Z0-9_~\\|,'\\-\\+:.()]+".r
val operatorTokens: Parser[String] = "[<>=!]+".r | ("IS NOT" | "IN" | "IS")
val conditionJoiner: Parser[String] = ( "AND" | "OR" )
val excludeKeywords = List("CASE","WHEN", "THEN", "ELSE", "END")
val identifierWithoutCaseKw: Parser[Exp] = Parser(input =>
identifiers(input).filterWithError(
!excludeKeywords.contains(_),
reservedWord => s"$reservedWord encountered",
input
)
) ^^ StrExp
val anyStrExp: Parser[Exp] = "[^()]*".r ^^ StrExp
val funcIdentifier: Parser[Exp] = name ~ ("(" ~> (caseConditionExpresionParser | funcIdentifier | anyStrExp) <~ ")") ^^ {case func ~ param => FunCallExp(func, Seq(param))}
val identifierOrFunctions = funcIdentifier | identifierWithoutCaseKw
val conditionParser: Parser[String] =
identifierOrFunctions ~ operatorTokens ~ identifierOrFunctions ~ opt(conditionJoiner) ^^ {
case a ~ op ~ b ~ c => s"$a $op $b ${c.getOrElse("")}"
}
def caseConditionExpresionParser: Parser[CaseConditions] = "CASE" ~ rep1("WHEN" ~ rep(conditionParser) ~ "THEN" ~ rep(identifierWithoutCaseKw)) ~ "ELSE" ~ rep(identifierWithoutCaseKw) ~ "END" ~ opt("AS" ~> identifierWithoutCaseKw)^^ {
case "CASE" ~ conditionValuePair ~ "ELSE" ~ falseValue ~ "END" ~ asName =>
CaseConditions(
conditionValuePair.map(cv => (
cv._1._1._2.mkString(" "),
parsePipes(cv._2.mkString(" ")).isRight match {
case true => parsePipes(cv._2.mkString(" ")).right.get
case _ => cv._2.mkString(" ")
}
)),
parsePipes(falseValue.mkString("")).isRight match {
case true => parsePipes(falseValue.mkString(" ")).right.get
case _ => falseValue.mkString("")
}, asName)
}
//this parser can be used to get the results
val caseExpression = caseConditionExpresionParser | funcIdentifier
def parsePipes(input: String): Either[Seq[ParsingError], String] = {
parse(caseExpression, input) match {
case Success(parsed, _) => Right(parsed.asStr)
case Failure(msg, next) => Left(Seq(ParsingError(s"Failed to parse $pipedStr: $msg, next: ${next.source}.")))
case Error(msg, next) => Left(Seq(ParsingError(s"Error in $pipedStr parse: $msg, next: ${next.source}.")))
}
}

How to do pattern matching on regex in a foreach function in scala?

I don't understand why this doesn't work (i have two "no match" here) :
val a = "aaa".r
val b = "bbb".r
List("aaa", "bbb").foreach {
case a(t) => println(t)
case b(t) => println(t)
case _ => println("no match")
}
Variable in parentheses is supposed to be capturing group.
Change your regexes to val a = "(aaa)".r; val b = "(bbb)".r, that'll make it do what you want.
Alternatively, change the match patterns:
List("aaa", "bbb").foreach {
case a() => println("aaa")
case b() => println("bbb")
case _ => println("no match")
}
Your pattern contains no capture group, you need to put parenthesis around the pattern you want to capture in order for the the pattern matching to work:
val a = "(aaa)".r
// a: scala.util.matching.Regex = (aaa)
val b = "(bbb)".r
// b: scala.util.matching.Regex = (bbb)
List("aaa", "bbb").foreach {
case b(t) => println(t)
case a(t) => println(t)
case _ => println("no match")
}
//aaa
//bbb

Sort depending on variable in Scala

Is there any way in Scala to sort a list of objects by a specific field using a variable to set the order (ASC or DESC)?
I know with sortWith you can do something like
myList.sortWith((x, y) => x < y)
or
myList.sortWith((x, y) => x > y)
to sort ascending or descending, but I want to use a variable.
So, I tried something like this:
case class Person(firstName: String, LastName: String, age: Int)
private def sortDocuments(sortField: String, sortDirection: String, people: List[Person]): List[Person] = {
sortField match {
case "age" => people.sortBy(if (sortDirection == "desc") -_.age else _.age)
case "firstName" => people.sortWith { sortString(a.firstName, b.firstName, sortDirection) }
case "lastName" => people.sortWith { sortString(a.firstName, b.lastName, sortDirection) }
}
}
private def sortString(fieldA: String = null, fieldB: String = null, direction: String = "asc") = {
val fieldAVaild = Option(fieldA).getOrElse("")
val fieldBVaild = Option(fieldB).getOrElse("")
if (direction == "desc") fieldBVaild > fieldAVaild else fieldAVaild < fieldBVaild
}
But sortWith only receives a function with two parameters, so I get an error when I add the third parameter (sortDirection).
You forgot the (a, b) => expr part of the first/last name cases
case "firstName" => people.sortWith {(a, b) => sortString(a.firstName, b.firstName, sortDirection) }

Regex not matching

I expect regex "[a-zA-Z]\\d{6}" to match "z999999" but it is not matching as an empty List is mapped :
val lines = List("z999999"); //> lines : List[String] = List(z999999)
val regex = """[a-zA-Z]\d{6}""".r //> regex : scala.util.matching.Regex = [a-zA-Z]\d{6}
val fi = lines.map(line => line match { case regex(group) => group case _ => "" })
//> fi : List[String] = List("")
Is there a problem with my regex or how I'm using it with Scala ?
val l="z999999"
val regex = """[a-zA-Z]\d{6}""".r
regex.findAllIn(l).toList
res1: List[String] = List(z999999)
The regex seems valid.
lines.map( _ match { case regex(group) => group; case _ => "" })
res2: List[String] = List("")
How odd. Let's see what happens with a capturing group around the whole expression we defined in regex.
val regex2= """([a-zA-Z]\d{6})""".r
regex2: scala.util.matching.Regex = ([a-zA-Z]\d{6})
lines.map( _ match { case regex2(group) => group; case _ => "" })
res3: List[String] = List(z999999)
Huzzah.
The unapply method on a regex is for getting the results of capturing groups.
There are other methods on a regex object that just get matches (e.g. findAllIn, findFirstIn, etc)