Función slice En Kotlin

En este tutorial aprenderás a cómo usar la función slice en Kotlin con la finalidad de seleccionar los elementos de una colección, según los índices pasados como argumento.

Función slice()

La función de extensión slice() retorna los elementos de una colección especificados por sus índices. Es decir, toma como parámetro una colección de índices enteros (IntRange o Iterable<Int>) para seleccionar los ítems y luego arroja como resultado instancia de List<T>. Las siguientes sintaxis muestran esta definición:

// slice() en arreglos genéricos
fun <T> Array<out T>.slice(indices: IntRange): List<T>
fun <T> Array<out T>.slice(indices: Iterable<Int>): List<T>

// slice() en colecciones
fun <T> List<T>.slice(indices: IntRange): List<T>
fun <T> List<T>.slice(indices: Iterable<Int>): List<T>

Considera la lista de enteros L = [1, 2, 3, 4, 5] y el uso de la función slice() en al siguiente ilustración:

Ejemplo de la función slice en Kotlin

Al usar como parámetro el rango 0..1 estamos indicando que deseamos obtener los elementos con el índice 0 y 1 de la lista. Lo que resulta en la nueva lista R = [1, 2]. El siguiente código muestra la representación de este escenario:

fun main() {
    val L = listOf(1, 2, 3, 4, 5)
    val R = L.slice(0..1)
    println(R)
}

Salida:

[1, 2]

El rango anterior acota solo dos valores. Esta forma no aprovecha el poder de la clase IntRange, por lo podrías usar un conjunto para mejorar la legibilidad:

val R = L.slice(setOf(0, 1))

Obtener Los Tres Elementos Centrales De Un Arreglo

Veamos otro ejemplo de la función slice() en arreglos.

Tomemos como ilustración un arreglo de enteros del cual deseamos obtener los tres elementos del centro. Luego de ello, se debe calcular el elemento menor de los tres.

fun main() {
    val numbers = listOf(3, 0, 1, 5, 2, 6, 1) // 1
    val centerOfNumbers = numbers.size / 2 // 2
    val centerIndices = centerOfNumbers - 1..centerOfNumbers + 1 // 3
    val threeInTheCenter = numbers.slice(centerIndices) // 4
    println("El valor más pequeño de $threeInTheCenter es ${threeInTheCenter.minOrNull()}") // 5
}

Salida:

El valor más pequeño de [1, 5, 2] es 1

Solución:

  1. En esta línea se declara el arreglo de enteros numbers. El tamaño elegido es de siete para acomodar a nuestra conveniencia de la obtención del centro.
  2. Calculamos el centro dividiendo el tamaño de numbers por 2.
  3. Declaramos un rango con los tres índices del centro. El límite inferior será el centro menos la unidad y el superior el centro más la unidad.
  4. Obtenemos los elementos en el centro pasando centerIndices a la función slice(), invocada desde numbers.
  5. Imprimimos un mensaje que indique cual es el valor mínimo de los elementos del centro. Para obtener dicha cantidad usamos la función minOrNull(). Como es evidente, el resultado será el literal 1.

Eliminar Elementos Seleccionados

Pensemos en un programa que permite ver una lista de archivos disponibles al usuario. Basado en ello se desea implementar una característica que permita seleccionar archivos y luego enviarlos a un servidor.

La siguiente es la simulación en consola de este comportamiento:

val files = mutableListOf(
    "proyecto1.rar",
    "test.txt",
    "datos2021.xls",
    "otros.pdf",
    "ingresos.txt"
)

fun main() {  
    showFiles()

    var isInvalidUserInput = true
    while (isInvalidUserInput) {
        print("Escriba los índices: ")
        val fileIndices = processUserInput(readLine())

        if (fileIndices.isNotEmpty()) {
            isInvalidUserInput = false
            sendFiles(fileIndices)
        } else {
            println("¡Entrada inválida!\n")
        }
    }
}

private fun showFiles() {
    files.forEachIndexed { index, s -> println("${index + 1}. $s") }
}

private fun processUserInput(userInput: String?) = try {
    convertUserInput(userInput)
} catch (e: Exception) {
    emptySet()
}

private fun sendFiles(fileIndices: Set<Int>) {
    val selectedFiles = files.slice(fileIndices)
    println("Archivos a enviar:$selectedFiles")
}

private fun convertUserInput(userInput: String?): Set<Int> {
    if (userInput.isNullOrBlank()) {
        throw RuntimeException()
    }

    return if (isASingleIndex(userInput)) {
        convertInputToIndex(userInput)
    } else {
        val inputParts = splitIntoIndices(userInput)
        convertInputToIndices(inputParts)
    }
}

private fun isASingleIndex(cleanInput: String) = cleanInput.length == 1

private fun convertInputToIndex(cleanInput: String): Set<Int> {
    val index = cleanInput.trim().toInt() - 1
    return if (isValidIndex()(index)) {
        setOf(index)
    } else {
        throw RuntimeException()
    }
}

private fun splitIntoIndices(userInput: String): List<String> = userInput.split(',')

private fun convertInputToIndices(inputParts: List<String>): Set<Int> {
    val integerParts = inputParts.map { it.trim().toInt() - 1 }
    return integerParts.filter(isValidIndex()).toSet()
}

fun isValidIndex() = { i: Int -> i in 0..files.size }

Salida:

1. proyecto1.rar
2. test.txt
3. datos2021.xls
4. otros.pdf
5. ingresos.txt
Escriba los índices: 1,3,5
Archivos a enviar:[proyecto1.rar, datos2021.xls, ingresos.txt]

La solución escrita en la función main() consiste de un bucle while que se ejecuta mientras la entrada del usuario sea inválida.

El bucle comienza con la impresión de los archivos disponibles con showFiles() y luego pide al usuario los índices para seleccionar los elementos a enviar.

La entrada del usuario es procesada con processUserInput(). Donde se convertirá la entrada con convertUserInput() en un Set<Int> de índices.

Si es un solo elemento, entonces convertimos con convertInputToIndex(), de lo contrario con convertInputToIndices() para realizar un split() según el delimitador coma (',').

Al final del procesamiento, el bucle while evalúa si hubo un resultado que no sea vacío, Si es así entonces mostramos los archivos a enviar en sendFiles(). Aquí usamos a slice() para tomar los índices construidos e imprimirlos en pantalla.

Si no se cumple la condición, entonces se imprimirá un mensaje sobre que la entrada es inválida y así intentar de nuevo.

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!