Get an element with its index counting from the end in Kotlin

Viewed 6612

Is there a simple function to get an element with its index counting from the end in a Kotlin Array, List, or String? In other words, is there a Kotlin equivalent of negative index slicing in Python?

3 Answers

There is no direct function for slicing, but one can write an user defined function or easily simulate using reversed function, that works with string, lists and arrays.

Strings In Python

pyString = 'Python'
sObject = slice(-1, -4, -1)
print(pyString[sObject])    # output: noh

Strings In Kotlin

val pyString = "Python"
val sObject = pyString.reversed().substring(0, 3).reversed()  // index 3 excluded
println(pyString)    // output: noh

List(or Arrays) in Kotlin

var py = arrayOf(1, 2, 3, 4, 5, 6, 7, 8)
var sObject = py.reversed().slice(0..2).reversed()
println(sObject)

However you can do method or function overload, using this as implicit object

For instance, you can program reverse substring, but here you cannot use negative numbers, because we need a different parameters profile regarding to the original method:

fun String.substring(a: Int, b: Int = 0, rev: Boolean): String {
    if (rev == true)
        if (b == 0)
            return this.substring(0, this.length - a)
        else
            return this.substring(this.length - b, this.length - a)
    else
        if (b == 0)
            return this.substring(a)
        else
            return this.substring(a, b)
}

So "whale".substring(0,2,true) is "le"

You can use similar technique to extend slice method.

I implemented a function similar to python's slicing mechanism

import kotlin.math.abs


fun String.substring(startingIndex: Int, endingIndex: Int, step: Int=1): String {
    var start = startingIndex
    var end = endingIndex
    var string = this

    if (start < 0) {
        start = string.length + start
    }
    if (end < 0) {
        end = string.length + end
    }
    
    if (step < 0) {
        string = string.reversed()
    }
    
    if (start >= string.length) {
        throw Exception("Index out of bounds.")
    }
    
    var outString = ""
    
    for ((index, character) in string.withIndex()) {
        if (index % abs(step) == 0) {
            if (index >= start && index <= end) {
                outString += character
            }
        }
    }
    return outString
}

Can be used like this:

println("This is some text.".substring(8, -6))  // some

There is but on problem with my function, it is that a negative step messes it up if you also use negative indexes.

These answers are quite complicated. If you just want to use a negative index, all you have to do is exampleList[exampleList.lastIndex - exampleIndex] which will (basically) do the same as exampleList[-exampleindex] in python. So for example you would do:

fun main(args: Array<String>) {
    val exampleList = mutableListOf<Int>(1, 2, 3, 4, 5)
    //Now we want exampleList[-1] or 4. I know in python it would be writen as exampleList[-2], but don't worry about that!
    println(exampleList[exampleList.lastIndex - 1])
}

the lastIndex parameter is the key thing to remember.

Related