Melllvar
Melllvar

Reputation: 2086

Component destructuring with fewer than expected components

Let's say I want to do the following:

val (k, v) = pair.split("=".toRegex(), 2)

This code is fine if I always get 2 components from the split - however, if the delimiter is not present in the string, this code throws an exception, because the second element in the array isn't present.

The answer is almost certainly "no", but is there some way to coerce destructure to assign null values to missing components?

Upvotes: 10

Views: 2248

Answers (4)

djsutho
djsutho

Reputation: 5834

It doesn't cover as many cases as other answers (also might not be as obvious what's happening) but you can always force there to be at least the correct number of values to destructure (extra values will be ignored). Using your example you can just add null to increase the size of the list returned by split:

val (k, v) = "foo=bar".split("=".toRegex(), 2) + null
> k=foo, v=bar

val (k, v) = "foo".split("=".toRegex(), 2) + null
> k=foo, v=null

Playground example https://pl.kotl.in/W7gGYyAjC

Upvotes: 0

shiftpsh
shiftpsh

Reputation: 1926

When destructuring objects, Kotlin calls componentN() for that object. For arrays, component1() is equal to get(0), component2() is equal to get(1), and so on.

So if the index is out of bounds, it'll throw ArrayIndexOutOfBoundsException, instead of returning null.


But you can make your operator function like this:

operator fun <T> Array<out T>.component1(): T? = if (size > 0) get(0) else null
operator fun <T> Array<out T>.component2(): T? = if (size > 1) get(1) else null

so if I run

val (k, v) = arrayOf(1)

println(k)
println(v)

the output will be

1
null

See:

Upvotes: 9

zsmb13
zsmb13

Reputation: 89608

You could add your own extension to List that adds the required number of null values to the end:

val (k, v) = pair.split("=".toRegex(), 2).padWithNulls(limit = 2)

Implementation can be done a couple of ways, here's just one:

private inline fun <reified E> List<E>.padWithNulls(limit: Int): List<E?> {
    if (this.size >= limit) {
        return this
    }

    val result: MutableList<E?> = this.toMutableList()
    result.addAll(arrayOfNulls(limit - this.size))
    return result
}

Here's a simpler one as well:

private fun <E> List<E>.padWithNulls(limit: Int): List<E?> {
    val result: MutableList<E?> = this.toMutableList()

    while (result.size < limit) {
        result.add(null)
    }

    return result
}

Or wrapping this functionality even further:

val (k, v) = pair.splitAndPadWithNulls("=".toRegex(), 2)

private fun String.splitAndPadWithNulls(regex: Regex, limit: Int): List<String?> {
    return this.split(regex, limit).padWithNulls(limit)
}

Upvotes: 3

Sasi Kumar
Sasi Kumar

Reputation: 13348

Its working for me

val pair="your string"
if(pair.isNotEmpty()&&pair.contains("=")) {
    val (k, v) = pair.split("=".toRegex(), 2)
    println(k)
    println(v)
}

Upvotes: 0

Related Questions