Home > Software engineering >  split list of objects by delimiter in Kotlin
split list of objects by delimiter in Kotlin

Time:07-16

I have a List of objects I want to split by a delimiter into sublists, e.g:

val tokens = listOf(
    Token(name = "lorem", val = "ipsum"),
    Token(name = "dolor", val = "sit"),
    Token(name = "newline", val = "\n"),
    Token(name = "amet", val = "consectetur")
)

The delimiter should be any Token whose name is "newline", so after the split, tokens should become:

listOf(
    listOf(
        Token(name = "lorem", val = "ipsum"),
        Token(name = "dolor", val = "sit")
    ),
    listOf(
        Token(name = "amet", val = "consectetur")
    )
)

I've written my own function to do this already, but I was wondering if there was some elegant, built-in (preferably functional) way of doing it. I say this because I'm learning Kotlin and, coming from C , find myself "reinventing the wheel" a lot with these types of things.

CodePudding user response:

I think there isn't any extension function in standard library for handling this case. You will have to write your own logic. You can do something like this:

val newList = mutableListOf<List<Token>>()
var subList = mutableListOf<Token>()
for (token in tokens) {
    if (token.name == "newline") {
        newList  = subList
        subList = mutableListOf()
    } else {
        subList  = token
    }
}
if (subList.isNotEmpty())
    newList  = subList
println(newList)

You can also extract this code out in the form of an extension function:

fun <T> List<T>.split(delimiter: (T) -> Boolean): List<List<T>> {
    val newList = mutableListOf<List<T>>()
    var subList = mutableListOf<T>()
    for (token in this) {
        if (delimiter(token)) {
            newList  = subList
            subList = mutableListOf()
        } else {
            subList  = token
        }
    }
    if (subList.isNotEmpty())
        newList  = subList
    return newList
}

// Usage
fun main() {
    val tokens = listOf(
        Token(name = "lorem", val = "ipsum"),
        Token(name = "dolor", val = "sit"),
        Token(name = "newline", val = "\n"),
        Token(name = "amet", val = "consectetur")
    )
    println(tokens.split { it.name == "newline" })
}

CodePudding user response:

You can use fold:

tokens
    .fold(mutableListOf(mutableListOf<Token>())) { l, elem ->
        if(elem.name=="newline") l.add(mutableListOf())
        else l.last().add(elem)
        l
    }

The first parameter is the initial value, a list with a single list in it (If there is no newline, you still want to have a single list containing the elements).

The second parameter is a function that is ececuted for every element.

If the token name is newline, it adds a new list. If not, it adds the element to the last list.

The last line of fold containing l makes sure that the list is returned.

  • Related