In Scala it’s very easy to perform pattern matching on a regex:
val regex = "varchar\\((\\d+)\\)".r
val string = "varchar(255)"
string match {
case regex(precision) => println(precision)
case other => println("not a varchar")
}
What’s the closest equivilent in Kotlin? Something like using a regex in a when would be ideal, but if not, then other tips would be good.
3 Likes
I would write
val regex = Regex("varchar\\((\\d+)\\)")
val string = "varchar(255)"
val match = regex.matchEntire(string)
if (match != null) {
val (precision) = match.destructured
println(precision)
} else {
println("not a varchar")
}
If you don’t need “else” branch, it might be shorter:
regex.matchEntire(string)?.let { match ->
val (precision) = match.destructured
println(precision)
}
Not as short as in Scala, unfortunately.
1 Like
I can of course use an if else branch. I wasn’t asking how to do regex matching. I was wondering if there was a nice easy way of doing it in a when clause like a Scala pattern match. The reason being is I want to do something like
when (str) {
"a" ->
"b" ->
regex(c) ->
}
2 Likes
There are two forms of when
. With when (str)
you can only do a direct comparison. And simple when
is a replacement for a series of if
-s, so it’s not better either. Scala is much more powerful in that aspect, you can’t do unapply
in Kotlin, unfortunately. There are some rudimentary forms with destructuring operator, but it can’t be used with when
and it is quite limited.
I think this is something that should be considered to be added. Pattern matching like this is one of the best parts of Scala and, imo, would be a welcome addition to Kotlin, in whatever form it would be.
5 Likes
operator fun Regex.contains(text: CharSequence): Boolean = this.matches(text)
when("Alex") {
in Regex(".+lex") -> { println("ends with lex") }
}
15 Likes
Just to throw in my own hack here:
fun Regex.capture(string: String): List<String>? = find(string)?.destructured?.toList()
inline fun <R> regexWhen(string: String, block: RegexWhen.() -> R): R = RegexWhen(string).block()
inline class RegexWhen(val regexWhenArg: String) {
inline fun <R: Any> Regex.then(block: (List<String>) -> R): R? = capture(regexWhenArg)?.let(block)
fun error(): Nothing = error("Unparsed string: $regexWhenArg")
}
(inline class
is optional, can be a normal class if your production environment deprecates experimental features)
Usage example:
val cell: String = regexWhen(arg) {
Regex("""R(\d+)C(\d+)""").then { (r, c) ->
"RC Cell: $r $c"
} ?:
Regex("""([A-Z]+)(\d+)""").then { (c, r) ->
"Excel Cell: $c $r"
} ?: "Unknown"
}
Basically a small DSL. The Elvis operators are a little awkward but necessary for lazy evaluation behavior. Note that if you actually want to return a null value from a “then” block you need to return@regexWhen null
to break out of the block.
You can also adapt it to your own use-cases; maybe you’d rather work with the entire MatchResult
instead, or do some other kind of pre-processing.
4 Likes
I’ve also come up with something here that you can use like:
whenMatch(line) {
bookingItemSecondLineRegex {
// Code
}
"Kontoauszug " {
// Code
}
otherwise {
// Code
}
}