Kotlin: Iterate through a list and combine same items

Viewed 1290

I'm writing an android app using Kotlin. I have a mutable list of strings and I want to iterate through that list in order to combine the same strings and show how many times they appear in the same order. For example,

current list = [a,b,b,b,c,d,d,a,a,b,b,c]

I want the new list to look like this:

new list = [a,(b) 3 times,c,(d) 2 times,(a) 2 times,(b) 2 times,c]

I know I can do this using a for-each loop but I can't seem to think of how as I'm new to programming. Would really appreciate if someone could help me with this!

3 Answers

Here's one way.

The results can be stored as pairs of the original type (in your case String but I made it generic so it can be flexible) to the count of their consecutive copies.

Create a MutableList to store the results. Create a variable for storing the most recently inspected element of the list, giving an initial value of the first element. Create another variable for tracking how many of the most recent element type have been found yet.

Then we iterate the list, starting at the second index (since we already recorded the first) and for each index we compare its value to the one we last inspected. If it's the same, we increase the count. If it's different, we can store the last inspected version and its count to the output list and reset the variables for the new type.

Finally add the last inspected value to the output list before returning it.

fun <T> List<T>.consolidateConsecutive(): List<Pair<T, Int>> {
    if (size == 0) 
        return emptyList()
    val outList = mutableListOf<Pair<T, Int>>()
    var current = this[0]
    var currentCount = 1
    for (i in 1..lastIndex){
        val value = this[i]
        if (current == value) {
            currentCount++
        } else {
            outList.add(current to currentCount)
            current = value
            currentCount = 1
        }
    }
    outList.add(current to currentCount)
    return outList
}

Usage:

fun main() {
    val list = listOf("a", "b", "b", "b", "c", "d", "d", "a", "a", "b", "b", "c")
    val consolidated = list.consolidateConsecutive()
    println(consolidated.joinToString())
}

prints

(a, 1), (b, 3), (c, 1), (d, 2), (a, 2), (b, 2), (c, 1)

I have solved the task like Tenfour04, but with the usage of foldIndexed method, which is a part of the Kotlin Standard Library. The foldIndexed method is in the official documentation described as:

Accumulates value starting with initial value and applying operation from left to right to current accumulator value and each element with its index in the original array.

Basically it iterates over a given collection (actual element is one of the parameters) and if needed, stores the element into an accumulator (array, list, etc., given as an argument).

Here is the code:

fun <T> consolidadeConsecutive(currentList: List<T>): List<Pair<T, Int>>
{
    if(currentList.isEmpty()) return emptyList()
    var currentCount = 0
    var currentElem = currentList.first()

    return currentList.foldIndexed(mutableListOf<Pair<T, Int>>()) { idx, acc, act ->
        if(act == currentElem) currentCount++
        else {
            acc.add(Pair(currentElem, currentCount))
            currentCount = 1
            currentElem = act
        }
        if(idx == currentList.lastIndex) acc.add(Pair(currentElem, currentCount))
        acc
    }     
}

fun main() {
    val currentList = listOf("a", "b", "b", "b", "c", "d", "d", "a", "a", "b", "b", "c")
    println(consolidadeConsecutive(currentList).joinToString())  
}

Prints:

(a, 1), (b, 3), (c, 1), (d, 2), (a, 2), (b, 2), (c, 1)

Here's my take. I use fold() to build up the list of pairs. If we get a duplicate, increment the count of the last pair.

val pairs = listOf("a", "b", "b", "b", "c", "d", "d", "a", "a", "b", "b", "c")
    .fold(mutableListOf<Pair<String, Int>>()) { list, curr ->
        list.apply {
            // Get the previous element
            val (prev, count) = lastOrNull() ?: null to 0
            if (prev == curr) {
                // If we got the same element, increment the count, replacing the last pair
                removeAt(lastIndex)
                add(curr to count + 1)
            } else {
                // Otherwise add a new pair to the list
                add(curr to 1)
            }
        }
    }

println(pairs)

Result:

[(a, 1), (b, 3), (c, 1), (d, 2), (a, 2), (b, 2), (c, 1)]
Related