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
use removeAll
pushList?.removeAll { TimeUnit.MILLISECONDS.toMinutes(
System.currentTimeMillis() - it.date) > THRESHOLD }
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()
}
}
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 Iterators 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
}
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") }
}
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.
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
}
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
}
}
}
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
}
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.
Related
I'm learning data structures and trying to implement a dynamic array from scratch in Kotlin using generics. I came up with the following implementation using a MutableList but that feels like cheating 😅. Am I doing this correctly or is there another/better way that allows me to learn by implementing the individual operations manually? What's the usual way others go about this?
class DynamicArray<T>(
private var values: MutableList<T>
) {
var length: Int = values.size
private set
var isEmpty: Boolean = length > 0
private set
fun getValues() = values
// O(1) time complexity
fun lookup(index: Int) = values[index]
// O(1) time complexity
fun push(item: T): MutableList<T> {
values.add(length, item)
length++
return values
}
// O(n) time complexity because we have to shift remaining items
fun remove(item: T): MutableList<T> {
values.remove(item)
length--
return values
}
}
Specifically you're saying that in order for your DynamicArray to exist, you need an already implemented mutable list (dynamic array) structure to rely upon.
So if you're trying to learn how these data structures are made, you should try to make one instead of using one. At the moment you're just delegating the difficult parts to someone else's work.
Try implementing this using only the Array type to make an ArrayList style implementation, or try not using an Array at all to implement a LinkedList type structure.
Here's a quick implementation that uses Array as the underlying DS.
It implements a MutableIterable which is basically an Iterable that also allows you to remove an element while iterating.
#Suppress("UNCHECKED_CAST")
class DynamicArray<T>(
private var size: Int,
private val expansionFactor: Float = 2f,
private val init: (Int) -> T) : MutableIterable<T> {
var capacity: Int = size
private set
private var arr: Array<Any?> = Array(size, init)
inner class DynamicArrayIterator: MutableIterator<T>{
val iterator = arr.iterator()
var index = -1
override fun hasNext(): Boolean {
return index<size-1
}
override fun next(): T {
if(iterator.hasNext()) index++
else throw NoSuchElementException()
return iterator.next() as T
}
override fun remove() {
removeAt(index--)
}
}
override fun iterator(): MutableIterator<T> = DynamicArrayIterator()
fun size(): Int = size
//O(1)
fun get(index: Int): T {
if(index > size-1) throw IndexOutOfBoundsException()
return arr[index] as T
}
//O(1)
fun set(index: Int, element: T) {
if(index > size-1) throw IndexOutOfBoundsException()
arr[index] = element
}
//O(1) amortized
fun add(element: T){
if(size == capacity){
capacity = (expansionFactor*size).toInt()
val newArr = Array<Any?>(capacity, init = init)
arr.forEachIndexed { index, any -> newArr[index] = any as T }
arr = newArr
}
arr[size] = element
size++
}
//O(n)
fun removeAt(index: Int): T{
if(index > size-1) throw IndexOutOfBoundsException()
val element = arr[index] as T
for( i in index until size){
arr[i] = arr[i+1]
}
size--
return element
}
//O(n)
fun remove(element: T): Boolean{
for(i in 0 until size){
if(arr[i] == element) {
removeAt(i)
return true
}
}
return false
}
//O(n)
override fun toString(): String {
val stringBuilder = StringBuilder()
stringBuilder.append("[")
var index = 0
do{
if (index in 1 until size)
stringBuilder.append(", ")
stringBuilder.append(arr[index])
index++
} while (index < size)
return stringBuilder.append("]").toString()
}
}
I have a toggleItem algorithm which removes/adds item from/to a recyclerview items list. It was optimised by a friend of mine to reduce the code smell that comes with !! .
I'm looking for alternative / minimal ways I can write this simple algorithm, with the awesome kotlin collections operations that we have. What's your optimum/minimal alternative to these two?
.
(ItemType is an enum class, as a tag of initialised item)
Original:
fun toggleItem(itemType: ItemType, show: Boolean) {
val item = _allItems.value?.find { it.type == itemType }
item?.let {
if (!show) _carouselItems.value = _carouselItems.value!!.minus(it)
} ?: if (show) _carouselItems.value = _carouselItems.value!!.plus(item!!)
}
Further optimised :
fun toggleItem(itemType: ItemType, show: Boolean) {
if (show) {
val item = _allItems.value?.find { it.type == itemType }
item?.let {
_carouselItems.value = _carouselItems.value?.plus(it)?.distinct()?.sortedBy { it.type }
}
} else
_carouselItems.value = _carouselItems.value?.filter { it.type != itemType }
}
I’m inferring that _allItems and _carouselItems are both of type MutableLiveData<List<ItemType>?> or something very similar. You can replace your !! with an Elvis operator and default value to avoid the long chain of null-safe calls. It also is clearer to use an empty list to represent no data rather than using null so I would make the LiveData’s type non-nullable.
You can use the Elvis operator to do an early return / smart-cast in this case to avoid the use of let. A little more readable this way, in my opinion.
fun toggleItem(itemType: ItemType, show: Boolean) {
val currentItems = _carouselItems.value ?: emptyList()
if (show) {
val item = _allItems.value?.find { it.type == itemType }
?: return
_carouselItems.value = (currentItems + item).distinct().sortedBy { it.type }
} else
_carouselItems.value = currentItems.filter { it.type != itemType }
}
}
If you know the item should always be in the all items list, this could be more concise:
fun toggleItem(itemType: ItemType, show: Boolean) {
val item = _allItems.value?.find { it.type == itemType }
?: return
val currentItems = _carouselItems.value ?: emptyList()
_carouselItems.value = when {
show -> (currentItems + item).distinct().sortedBy { it.type }
else -> currentItems - item
}
}
You might also consider transitioning to StateFlow. It is a similar concept to LiveData, except that it enforces a beginning value, so its value parameter is not nullable if the type isn't nullable. But you should be familiar with coroutine basics first.
Both are ok when data set is small, but the computation will increase quickly as the data size grows.
// store visibility value to a map using ItemType as its keys
val _toggles: Map<ItemType, Boolean> = mapOf(ItemType.One to false, ItemType.Two to true);
fun toggleItem(itemType: ItemType, show: Boolean) {
_toggles[itemType] = show
val list = _allItem.values?.filter(item -> _toggles[item.type]) ?: listOf()
_carouselItems.value = list
}
I'm doing validation on an EditText. I want the CharSequence to be invalid if it's empty or it doesn't begin with "https://". I'm also using RxBinding, specifically RxTextView. The problem is that when there is one character left, and I then delete it leaving no characters left in the the CharSequence the map operator doesn't fire off an emission. In other words I want my map operator to return false when the EditText is empty. I'm beginning to think this may not be possible the way I'm doing it. What would be an alternative?
Here is my Observable / Disposable:
val systemIdDisposable = RxTextView.textChanges(binding.etSystemId)
.skipInitialValue()
.map { charSeq ->
if (charSeq.isEmpty()) {
false
} else {
viewModel.isSystemIdValid(charSeq.toString())
}
}
.subscribe { isValid ->
if (!isValid) {
binding.systemIdTextInputLayout.isErrorEnabled = true
binding.systemIdTextInputLayout.error = viewModel.authErrorFields.value?.systemId
} else {
binding.systemIdTextInputLayout.isErrorEnabled = false
binding.systemIdTextInputLayout.error = viewModel.authErrorFields.value?.systemId
}
}
And here is a function in my ViewModel that I pass the CharSequence to for validation:
fun isSystemIdValid(systemId: String?): Boolean {
return if (systemId != null && systemId.isNotEmpty()) {
_authErrors.value?.systemId = null
true
} else {
_authErrors.value?.systemId =
getApplication<Application>().resources.getString(R.string.field_empty_error)
false
}
}
After sleeping on it, I figured it out.
I changed RxTextView.textChanges to RxTextView.textChangeEvents. This allowed me to query the CharSequence's text value (using text() method provided by textChangeEvents) even if it's empty. Due to some other changes (not really relevant to what I was asking in this question) I was also able to reduce some of the conditional code too. I'm just putting that out there in case someone comes across this and is curious about these changes. The takeaway is that you can get that empty emission using RxTextView.textChangeEvents.
Here is my new Observer:
val systemIdDisposable = RxTextView.textChangeEvents(binding.etSystemId)
.skipInitialValue()
.map { charSeq -> viewModel.isSystemIdValid(charSeq.text().toString()) }
.subscribe {
binding.systemIdTextInputLayout.error = viewModel.authErrors.value?.systemId
}
And here is my validation code from the ViewModel:
fun isSystemIdValid(systemId: String?): Boolean {
val auth = _authErrors.value
return if (systemId != null && systemId.isNotEmpty()) {
auth?.systemId = null
_authErrors.value = auth
true
} else {
auth?.systemId =
getApplication<Application>().resources.getString(R.string.field_empty_error)
_authErrors.value = auth
false
}
}
Lastly, if anyone is curious about how I'm using my LiveData / MutableLiveData objects; I create a private MutableLiveData object and only expose an immutable LiveData object that returns the values of the first object. I do this for better encapsulation / data hiding. Here is an example:
private val _authErrors: MutableLiveData<AuthErrorFields> by lazy {
MutableLiveData<AuthErrorFields>()
}
val authErrors: LiveData<AuthErrorFields>
get() { return _authErrors }
Hope this helps someone! 🤗
I wanna sort some strings that contain numbers but after a sort, it becomes like this ["s1", "s10", "s11", ... ,"s2", "s21", "s22"]. after i search i fount this question with same problem. but in my example, I have mutableList<myModel>, and I must put all string in myModel.title for example into a mutable list and place into under code:
val sortData = reversedData.sortedBy {
//pattern.matcher(it.title).matches()
Collections.sort(it.title, object : Comparator<String> {
override fun compare(o1: String, o2: String): Int {
return extractInt(o1) - extractInt(o2)
}
fun extractInt(s: String): Int {
val num = s.replace("\\D".toRegex(), "")
// return 0 if no digits found
return if (num.isEmpty()) 0 else Integer.parseInt(num)
}
})
}
I have an error in .sortedBy and Collections.sort(it.title), may please help me to fix this.
you can use sortWith instead of sortBy
for example:
class Test(val title:String) {
override fun toString(): String {
return "$title"
}
}
val list = listOf<Test>(Test("s1"), Test("s101"),
Test("s131"), Test("s321"), Test("s23"), Test("s21"), Test("s22"))
val sortData = list.sortedWith( object : Comparator<Test> {
override fun compare(o1: Test, o2: Test): Int {
return extractInt(o1) - extractInt(o2)
}
fun extractInt(s: Test): Int {
val num = s.title.replace("\\D".toRegex(), "")
// return 0 if no digits found
return if (num.isEmpty()) 0 else Integer.parseInt(num)
}
})
will give output:
[s1, s21, s22, s23, s101, s131, s321]
A possible solution based on the data you posted:
sortedBy { "s(\\d+)".toRegex().matchEntire(it)?.groups?.get(1)?.value?.toInt() }
Of course I would move the regex out of the lambda, but it is a more concise answer this way.
A possible solution can be this:
reversedData.toObservable()
.sorted { o1, o2 ->
val pattern = Pattern.compile("\\d+")
val matcher = pattern.matcher(o1.title)
val matcher2 = pattern.matcher(o2.title)
if (matcher.find()) {
matcher2.find()
val o1Num = matcher.group(0).toInt()
val o2Num = matcher2.group(0).toInt()
return#sorted o1Num - o2Num
} else {
return#sorted o1.title?.compareTo(o2.title ?: "") ?: 0
}
}
.toList()
.subscribeBy(
onError = {
it
},
onSuccess = {
reversedData = it
}
)
As you state that you need a MutableList, but don't have one yet, you should use sortedBy or sortedWith (in case you want to work with a comparator) instead and you get just a (new) list out of your current one, e.g.:
val yourMutableSortedList = reversedData.sortedBy {
pattern.find(it)?.value?.toInt() ?: 0
}.toMutableList() // now calling toMutableList only because you said you require one... so why don't just sorting it into a new list and returning a mutable list afterwards?
You may want to take advantage of compareBy (or Javas Comparator.comparing) for sortedWith.
If you just want to sort an existing mutable list use sortWith (or Collections.sort):
reversedData.sortWith(compareBy {
pattern.find(it)?.value?.toInt() ?: 0
})
// or using Java imports:
Collections.sort(reversedData, Compatarator.comparingInt {
pattern.find(it)?.value?.toInt() ?: 0 // what would be the default for non-matching ones?
})
Of course you can also play around with other comparator helpers (e.g. mixing nulls last, or similar), e.g.:
reversedData.sortWith(nullsLast(compareBy {
pattern.find(it)?.value
}))
For the samples above I used the following Regex:
val pattern = """\d+""".toRegex()
I wrote a custom comparator for my JSON sorting. It can be adapted from bare String/Number/Null
fun getComparator(sortBy: String, desc: Boolean = false): Comparator<SearchResource.SearchResult> {
return Comparator { o1, o2 ->
val v1 = getCompValue(o1, sortBy)
val v2 = getCompValue(o2, sortBy)
(if (v1 is Float && v2 is Float) {
v1 - v2
} else if (v1 is String && v2 is String) {
v1.compareTo(v2).toFloat()
} else {
getCompDefault(v1) - getCompDefault(v2)
}).sign.toInt() * (if (desc) -1 else 1)
}
}
private fun getCompValue(o: SearchResource.SearchResult, sortBy: String): Any? {
val sorter = gson.fromJson<JsonObject>(gson.toJson(o))[sortBy]
try {
return sorter.asFloat
} catch (e: ClassCastException) {
try {
return sorter.asString
} catch (e: ClassCastException) {
return null
}
}
}
private fun getCompDefault(v: Any?): Float {
return if (v is Float) v else if (v is String) Float.POSITIVE_INFINITY else Float.NEGATIVE_INFINITY
}
is anyone facing this problem.
break or continue jump across class boundary kotlin
this problem appears when i am going to use break or continue. inside lambda with receiver i create 'letIn'
lambda with receiver code
fun letIn(componentName: String?, values: List<LifeService.Value?>?,
body: (String, List<LifeService.Value?>) -> Unit) {
if (!TextUtils.isEmpty(componentName) && (values != null && values.isNotEmpty())) {
body(componentName!!, values)
}
}
this sample code for it.
for (option in 0 until optionsSize) {
val component = optionsGroup?.options?.get(option)
component?.let {
with(component) {
letIn(presentation, values, { componentName, values ->
if (componentName == LifeComponentViewType.CHECKBOX) {
letIn(transformCheckBoxValues(optionsGroup), { data ->
dataSource?.push(componentName, ComponentDataCheckBoxCollection(name, data))
view.buildComponent(componentName)
// break or continue didnt work
})
} else {
dataSource?.push(componentName, ComponentDataCollection(name, values))
view.buildComponent(componentName)
}
})
}
}
}
because above code didnt work so i use imperative way.
for (option in 0 until optionsSize) {
val component = optionsGroup?.options?.get(option)
if (component != null) {
val presentation: String? = component.presentation
val values = component.values
if (!TextUtils.isEmpty(presentation)) {
if (presentation == LifeComponentViewType.CHECKBOX) {
val data = transformCheckBoxValues(optionsGroup)
if (data.isNotEmpty()) {
dataSource?.push(presentation, ComponentDataCheckBoxCollection(optionsGroup.name, data))
view.buildComponent(presentation)
return
}
} else {
dataSource?.push(presentation!!, ComponentDataCollection(component.name, values))
view.buildComponent(presentation!!)
}
} else {
return
}
}
}
does anyone have suggestions?
UPDATE
i've been fix this issue by inlining high order function.
(Other coding errors aside) You are seeing the error because inside your lambda, you cannot use break or continue to jump out of the lambda to the nearest loop. Instead, you can use a qualified return to jump out of the lambda to a label.
Referring to the language reference
The return-expression returns from the nearest enclosing function, i.e. foo. (Note that such non-local returns are supported only for lambda expressions passed to inline functions.) If we need to return from a lambda expression, we have to label it and qualify the return:
(Emphasis mine)
Your second example shows that you want your lambdas to do a non-local return from the enclosing function. Therefore, you do not need to qualify your return, but your function letIn must be declared inline (else you can only do a local, qualified return).
inline fun letIn(componentName: String?, values: List<LifeService.Value?>?,
body: (String, List<LifeService.Value?>) -> Unit) {
if (!TextUtils.isEmpty(componentName) && (values != null && values.isNotEmpty())) {
body(componentName!!, values)
}
}
... or if you want it to have receivers...
inline fun String?.letIn(values: List<LifeService.Value?>?,
body: String.(List<LifeService.Value?>) -> Unit) {
if (!TextUtils.isEmpty(this) && (values != null && values.isNotEmpty())) {
this!!.body(values)
}
}
When you declare letIn as inline, then you can place return in your lambdas without the compiler complaining. Your function would not need to be inline if your lambdas are only doing local returns, but it would need to have a qualified return (for example return#letIn).
Your first example would then look like this...
for (option in 0 until optionsSize) {
val component = optionsGroup?.options?.get(option)
component?.let {
with(component) {
presentation.letIn(values, { values ->
if (this == LifeComponentViewType.CHECKBOX) {
this.letIn(transformCheckBoxValues(optionsGroup), { data ->
dataSource?.push(this, ComponentDataCheckBoxCollection(this, data))
view.buildComponent(this)
return //returns from function
})
} else {
dataSource?.push(this, ComponentDataCollection(name, values))
view.buildComponent(this)
return //returns from function
}
})
}
}
}
Lastly, note that if you wanted to jump out of the lambda early, but continue an outer loop as in:
fun test1() {
val list = listOf("a", "b", "c")
val optionsSize = 2
for(i in 0..optionsSize) loop# {
println("calliing list.forEach")
list.forEach lit# {
if(it == "a") return#lit
if(it == "c") return#loop
println(it)
}
}
}
It won't work. Intelli-sense doesn't complain about it, but the compiler throws an internal error. But you can convert the outer loop to a lambda, and it does work...
fun test() {
val list = listOf("a", "b", "c")
val optionsSize = 2
(0..optionsSize).forEach() loop# {
println("calliing list.forEach")
list.forEach lit# {
if(it == "a") return#lit
if(it == "c") return#loop
println(it)
}
}
}
Again, this only works if the function to which the lambda is passed is declared inline (like forEach is declared inline).