Reputation: 525
I am new to kotlin programming. What I want is that I want to remove a particular data from a list while iterating through it, but when I am doing that my app is crashing.
for ((pos, i) in listTotal!!.withIndex()) {
if (pos != 0 && pos != listTotal!!.size - 1) {
if (paymentsAndTagsModel.tagName == i.header) {
//listTotal!!.removeAt(pos)
listTotal!!.remove(i)
}
}
}
OR
for ((pos,i) in listTotal!!.listIterator().withIndex()){
if (i.header == paymentsAndTagsModel.tagName){
listTotal!!.listIterator(pos).remove()
}
}
The exception which I am getting
java.lang.IllegalStateException
Upvotes: 47
Views: 49594
Reputation: 312
Another solution that will suit small collections. For example set of listeners in some controller.
inline fun <T> MutableCollection<T>.forEachSafe(action: (T) -> Unit) {
val listCopy = ArrayList<T>(this)
for (element: T in listCopy) {
if (this.contains(element)) {
action(element)
}
}
}
It makes sure that elements of collection can be removed safely even from outside code.
Upvotes: 0
Reputation: 6373
People didn't break iteration in previous posts dont know why. It can be simple but also with extensions and also for Map:
fun <T> MutableCollection<T>.removeFirst(filter: (T) -> Boolean) =
iterator().removeIf(filter)
fun <K, V> MutableMap<K, V>.removeFirst(filter: (K, V) -> Boolean) =
iterator().removeIf { filter(it.key, it.value) }
fun <T> MutableIterator<T>.removeFirst(filter: (T) -> Boolean): Boolean {
for (item in this) if (filter.invoke(item)) {
remove()
return true
}
return false
}
Upvotes: 1
Reputation: 3574
Typically you can use:
yourMutableCollection.removeIf { someLogic == true }
However, I'm working with an Android app that must support APIs older than 24. In this case removeIf can't be used.
Here's a solution that is nearly identical to that implemented in Kotlin Collections that doesn't rely on Predicate.test - which is why API 24+ is required in the first place
//This function is in Kotlin Collections but only for Android API 24+
fun <E> MutableCollection<E>.removeIff(filter: (E) -> Boolean): Boolean {
var removed = false
val iterator: MutableIterator<E> = this.iterator()
while (iterator.hasNext()) {
val value = iterator.next()
if (filter.invoke(value)) {
iterator.remove()
removed = true
}
}
return removed
}
Upvotes: 1
Reputation: 7194
val numbers = mutableListOf(1,2,3,4,5,6)
val numberIterator = numbers.iterator()
while (numberIterator.hasNext()) {
val integer = numberIterator.next()
if (integer < 3) {
numberIterator.remove()
}
}
Upvotes: 51
Reputation: 125
The following code works for me:
val iterator = listTotal.iterator()
for(i in iterator){
if(i.haer== paymentsAndTagsModel.tagName){
iterator.remove()
}
}
You can also read this article.
Upvotes: 10
Reputation: 811
Use a while loop, here is the kotlin extension function:
fun <E> MutableList<E>.removeIfMatch(isMatchConsumer: (existingItem: E) -> Boolean) {
var index = 0
var lastIndex = this.size -1
while(index <= lastIndex && lastIndex >= 0){
when {
isMatchConsumer.invoke(this[index]) -> {
this.removeAt(index)
lastIndex-- // max is decreased by 1
}
else -> index++ // only increment if we do not remove
}
}
}
Upvotes: 0
Reputation: 15744
use removeAll
pushList?.removeAll { TimeUnit.MILLISECONDS.toMinutes(
System.currentTimeMillis() - it.date) > THRESHOLD }
Upvotes: 37
Reputation: 2156
The answer by miensol seems perfect.
However, I don't understand the context for using the withIndex
function or filteredIndex
. You can use the filter
function just by itself.
You don't need access to the index the list is at, if you're using lists.
Also, I'd strongly recommend working with a data class if you already aren't. Your code would look something like this
Data Class
data class Event(
var eventCode : String,
var header : String
)
Filtering Logic
fun main(args:Array<String>){
val eventList : MutableList<Event> = mutableListOf(
Event(eventCode = "123",header = "One"),
Event(eventCode = "456",header = "Two"),
Event(eventCode = "789",header = "Three")
)
val filteredList = eventList.filter { !it.header.equals("Two") }
}
Upvotes: 24
Reputation: 41608
It's forbidden to modify a collection through its interface while iterating over it. The only way to mutate the collection contents is to use Iterator.remove
.
However using Iterator
s can be unwieldy and in vast majority of cases it's better to treat the collections as immutable which Kotlin encourages. You can use a filter
to create a new collections like so:
listTotal = listTotal.filterIndexed { ix, element ->
ix != 0 && ix != listTotal.lastIndex && element.header == paymentsAndTagsModel.tagName
}
Upvotes: 25