Home > Net >  starts with inside switch statement Kotlin
starts with inside switch statement Kotlin

Time:09-20

when(message) {
    .startsWith("hey")
}

How do I check if my string starts with a certain word inside a switch statement in Kotlin?

CodePudding user response:

You can use a when without an argument:

val msg = "This is some message"

when {
    msg.startsWith("This") -> println("Starts with This")
    msg.startsWith("That") -> println("Starts with That")
    else -> println("Doesn't start with This or That")
}

Output:

Starts with This

CodePudding user response:

This is really interesting question.

Maybe something like that, lol?

run {
    operator fun String.unaryPlus() = "This is some message".startsWith(this)
    when {
         "This" -> { println("Starts with This") }
         "That" -> { println("Starts with That") }
        else -> { println("Doesn't start with This or That") }
    }
}

run is for operator overload localization

Of course you can use function or extension function instead of operator.

fun String.a() = "This is some message".startsWith(this)
when {
    "This".a() -> { println("Starts with This") }
    "That".a() -> { println("Starts with That") }
    else -> { println("Doesn't start with This or That") }
}

PS. And of course you can write all this as library code ))

class PredicateContext<T>(private val predicate: (T) -> Boolean) {
    operator fun T.invoke() = predicate(this)
}

... and use it anywhere

PredicateContext<String> { "This is some message".startsWith(it) }.run{
    when {
        "This"() -> println("Starts with This")
        "That"() -> println("Starts with That")
        else -> println("Doesn't start with This or That")
    }
}

CodePudding user response:

You can't really do this and avoid the repetition, because when you provide an argument in when (something) the something is an expression that produces a value, which is fixed before the matching starts. And then your branch expressions are compared to that fixed value, trying to find an equals match (or a few other special cases, like is or in)

The closest you can get is what sirjoga is going for, where you don't provide an argument to when, so the branch conditions are just simple boolean expressions. So instead of each condition being a value trying to match that something value, you make a function call that produces a true or false value, and the when block uses the first one that matches true.


If you really need to avoid the repetition while keeping it looking like a standard when matcher, you could do something like this:

fun <T, R> when2(
    predicate: (T) -> Boolean,
    matchers: Map<T, () -> R>,
    default: () -> R
) : R
{
    val match = matchers.entries.firstOrNull { predicate(it.key) }?.value ?: default
    return match.invoke()
}

fun main() {
    val thing = "this is a sentence"
    
    when2({ thing.startsWith(it) }, 
        mapOf(
           "this" to { println("Starts with this") },
           "that" to { println("Starts with that") }
        ), default = { println("uh oh") }   
    )
}

But then you're creating a Map each time it's called, instead of getting compiled neatly into some basic conditional code. You could create a stateful object (so you pass this all into the function call, but it stores it and returns an object that lets you do checks):

fun <T, R> whenMaker(
    matchers: Map<T, () -> R>,
    default: () -> R
) : ((T) -> Boolean) -> R
{
    return { predicate ->
        val match = matchers.entries.firstOrNull { predicate(it.key) }?.value ?: default
        match.invoke()
    }
}

fun main() {    
    val match = whenMaker(
        mapOf(
           "this" to { println("Starts with this") },
           "that" to { println("Starts with that") }
        ), default = { println("uh oh") }   
    )
    match { "this is a sentence".startsWith(it) }
    match { "that is a sentence".startsWith(it) }
    match { "them is a sentence".startsWith(it) }
}

But now you're separating the definition of your when structure from where it's actually used - especially as ideally, you wouldn't be creating that match object inside your function since that could happen repeatedly. But, depending on what you're doing and how much it matters, something like this could be useful! E.g. a convenience class that defines the match object in a top-level variable, directly above the function that makes use of it might not be so bad.

CodePudding user response:

I found an answer on how to do it without tons of repetition. you can do it like this:

val str = "!settings change something"
val args = str.split(" ")

when (args[0]) {
    "!settings" -> TODO()
    "!other" -> TODO()
}

By doing it like this you don't have that much repetition because when having hundreds of cases you don't want to repeat writing msg.startsWith

  • Related