GettingStarted
GettingStarted

Reputation: 7625

Removing items in a MutableList with Kotlin

I could have a list like

 ["1", "2", "3", ".", "4", "."]

After the first occurrence of my delimiter, I want the duplicates removed

In my case, the above list should become

 ["1", "2", "3", ".", "4"]

I want all duplicates of "." removed after the first occurrence. Whats the best way?

Upvotes: 0

Views: 3693

Answers (5)

Adam Millerchip
Adam Millerchip

Reputation: 23147

Since you're using Kotlin, you have the advantage of immutable data types and functions without side-effects. Here's how to do it with an immutable list in a function that doesn't expose any state externally, by making use of fold():

val originalList = listOf("1", "2", "3", ".", "4", ".")

val (filteredList, _) = originalList.fold(
    Pair(emptyList<String>(), false)
) { (newList, found), item ->
    if (item == "." && !found) Pair(newList + item, true)
    else if (item == ".") Pair(newList, true)
    else Pair(newList + item, found)
}

println(filteredList)

Result:

[1, 2, 3, ., 4]

fold() takes an initial accumulator value, then applies the function for each element of list, updating the accumulator as it goes.

Here we set the accumulator to a Pair of an empty list, where we will build up the new list, and a boolean, to track if we've already seen ..

For each element of the original list, we return a new pair, adding the item to the new list (if necessary) and updating whether we have already seen .. newList + item doesn't add the item to the immutable list, it returns a new immutable list with the item appended to it. Because we pass the Bool tracking if we've seen . as part of the pair to each iteration, there's no need for a temporary variable outside the function to track this.

Finally, because the accumulated value is a Pair, we use destructuring to extract only the accumulated list (the first value) of the pair with val (filteredList, _) = pair.

For your list, the returned Pair values will look like this:

  1. ([1], false)
  2. ([1, 2], false)
  3. ([1, 2, 3], false)
  4. ([1, 2, 3, .], true)
  5. ([1, 2, 3, ., 4], true)
  6. ([1, 2, 3, ., 4], true)

Upvotes: 0

DikShU
DikShU

Reputation: 96

To remove these apply for loops and add the items in a new list.Steps

First Convert list into mutable list

 val list = listOf("1", "2", "3", ".", "4", ".")
 val mutablelist =list.toMutableList()

after this apply for loop and storing data in new Outcoming list

  val outcominglist= ArrayList<String>()

 for(i in list){
    val item = mutablelist[0]
    mutablelist.removeAt(0)
    if(outcominglist.contains(item)){

    }
    else{
        outcominglist.add(item)
    }
 }

To print Outcoming list.

print(outcominglist)

Second and the Simplest Method(Use .distinct Method)

 val list = listOf('1', '2', '3', '.', '4', '.')
 val newlist =list.distinct()
 print(newlist)

Upvotes: 0

Tenfour04
Tenfour04

Reputation: 93922

You can use a temporary MutableSet to easily check if values are duplicates.

fun <T> MutableList<T>.removeDuplicates(): Boolean {
    val set = mutableSetOf<T>()
    return retainAll { set.add(it) }
}

Explanation: MutableList.retainAll is a function that removes every item for which the lambda returns false. When you add an item to a Set, it returns false if the item already exists in the set. So the first occurrence of each unique item will return true while subsequent occurrences will return false


Edit: It occurred to me that maybe you are interested only in the specific delimiter entry having duplicates. In that case, instead of a Set, I would use just a Boolean to track if it's been found yet. And I use removeAll instead of retainAll to make it easier to read.

fun <T> MutableList<T>.removeDuplicatesOf(delimiter: T): Boolean {
    var firstInstanceFound = false
    return removeAll { it == delimiter && firstInstanceFound.also { firstInstanceFound = true } }
}

Explanation: removeAll will remove anything for which the lambda returns true. Due to logical short-circuiting, anything that isn't the delimiter will return false before the part after the && is reached. When the first delimiter is found, firstInstanceFound will be false, so the logical statement evaluates to false. The also branch is also hit, so firstInstanceFound will be true for any subsequent delimiters found.

Upvotes: 3

Ezequiel Zanetta
Ezequiel Zanetta

Reputation: 74

The easy way to do this is using distinct() function, that returns a list without duplicated values

val list = listOf('1', '2', '3', '.', '4', '.')
println(list.distinct()) // [1, 2, 3, ., 4]

Upvotes: 0

Steph
Steph

Reputation: 841

I found two ways. The first is the most 'Java':

// Setup values
val list = mutableListOf("1", "2", "3", ".", "4", ".")
val delim = "."

// Check if list is empty
val size = list.size - 1
if (size < 0) return

// Get first delim index
val firstMatch = list.indexOf(delim) + 1
if (firstMatch < 1) return

// Reverse-iterate the list until delim location
for (i in size downTo minOf(firstMatch, size)) {
    if (list[i] == delim) list.removeAt(i)
}

println(list)

Here is smaller Kotlin-style solution:

val list = mutableListOf("1", "2", "3", ".", "4", ".")
val delim = "."

val firstMatch = list.indexOf(delim)
if (firstMatch < 0) return
val newList = list.filterIndexed { index, s -> s != delim || index == firstMatch }
println(newList)

Upvotes: 1

Related Questions