Create list from two uneven lists

Issue

I’m new to Kotlin and it’s intricacies, but have two lists of unknown sizes and contents that could look something like this

codes = ["or", "or", "or", "parks", "parks", "wa", "wa", "wa", "id"]
types = ["STATE", "NATIONAL", "STATE", "STATE"]

Each type relates to a non-distinct item within codes (e.g. parks->NATIONAL, wa->STATE), but the total number of STATEs are needed. In this case, 7 STATEs i=are expected.

My initial thought was to do something like this

var typesIdx = 0
var prevCode = ""

val totalList = mutableListOf<String>()
    
for (currCode in codes) {
    if (currCode != prevCode) {
        prevCode = currCode
        typesIdx+=1
    }    
    totalList += types.get(typesIdx).toString()
} 

But I feel like there’s a better and smarter way to do this that implements more of Kotlin’s built in functions rather than simply for looping and creating the list bit-by-bit

Solution

If I have understood what you want to do correctly, you can use the distinct() method on a list to help here. It returns a list containing only distinct elements from the original list, preserving the order of appearance.

val codes = listOf("or", "or", "or", "parks", "parks", "wa", "wa", "wa", "id")
val types = listOf("STATE", "NATIONAL", "STATE", "STATE")

// First, condense the "codes" list down to its distinct entries - which
// should make it the same size as "Types"
val condensedCodes = codes.distinct()
println(condensedCodes) // ["or","parks","wa","id"]

// Then create a map from code to type
val typeMap = condensedCodes.zip(types).toMap()
println(typeMap) // {or=STATE, parks=NATIONAL, wa=STATE, id=STATE}

// Then use that map to count the original codes list based on type
val numStates = codes.count { typeMap[it] == "STATE" }
println(numStates) // prints 7

// or if you want the list of states
val states = codes.filter { typeMap[it] == "STATE" }
println(states) // [or, or, or, wa, wa, wa, id]

// or if you want to transform the codes list to a list of types
val typeOfCodes = codes.map { typeMap[it] }
println(typeOfCodes) // [STATE, STATE, STATE, NATIONAL, NATIONAL, STATE, STATE, STATE, STATE]

The approach above will not work if the same group of codes appears in multiple places in your list. You can’t use distinct any more, but it’s still possible with the following approach:

val codes = listOf("or", "or", "or", "parks", "parks", "wa", "wa", "id", "or", "or")
val types = listOf("STATE", "NATIONAL", "STATE", "STATE", "STATE")

val condensedCodes = codes.zipWithNext()
                          .filter { it.first != it.second }
                          .map { it.first } + codes.last()

How does this work? The zipWithNext() creates a list like this

[(or, or), (or, or), (or, parks), ...

then it gets filtered down to only the first elements from the mis-matched pairs, essentially selecting the last element of each set of repeats. The last group is missed this way, so then we add codes.last() on the end.

["or", "or", "or", "parks", "parks", "wa", "wa", "wa", "id"]
              ^              ^                    ^          
[            "or",          "parks",             "wa"      ] + "id"

If you were going to use this in a lot of places you could define an extension function (a neat feature of Kotlin) for lists

fun <T> List<T>.condense() = when(isEmpty()) {
    true -> listOf()
    else -> zipWithNext().filter { it.first != it.second }.map { it.first } + last()
}

to let you just use

val condensedCodes = codes.condense()

Answered By – Tyler V

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published