How to fix concurrent modification exception in Kotlin - android

This is my code, in which I am adding data to a list:
fun bindProcess(listOfDocId: MutableList<String>, isActvityLogEnabled: Boolean): Observable<MutableList<SearchResultModel>> {
return Observable.create<MutableList<SearchResultModel>> {
var filteredList =CopyOnWriteArrayList<SearchResultModel>()// mutableListOf<SearchResultModel>()
val headerItem: SearchResultModel = SearchResultModel()
headerItem.type = 0
headerItem.title = context.getString(R.string.top_matches_label)
filteredList.add(headerItem)
for (row in ContSyncApplication.getContacts()) {
if (listOfDocId.contains(row.docId)) {
val myData: SearchResultModel = SearchResultModel()
myData.type = 1
myData.docId = row.docId
myData.name = row.display_name
if (!row.profile_image.isNullOrBlank()) {
myData.imagePath = row.profile_image!!
}
filteredList.add(myData)
if (isActvityLogEnabled && DatabaseQueryHelper.checkActivityLogById(row.docId)) {
val myactivityData: SearchResultModel = SearchResultModel()
myactivityData.type = 1
myactivityData.docId = row.docId
myactivityData.name = row.display_name
myactivityData.imagePath = row.profile_image ?: ""
mActvityList.add(myactivityData)
}
}
}
if (mActvityList.size > 0) {
val activityHeader: SearchResultModel = SearchResultModel()
activityHeader.type = 0
activityHeader.title = "Activity Log"
filteredList.add(activityHeader)
filteredList.addAll(mActvityList)
}
it.onNext(filteredList)
it.onComplete()
}
}
While I execute and add data in the list I am getting a ConcurrentModificationException. Can anyone suggest to me how to execute the loop so that I can fix this issue? I have to search and add to the list.

Check if you are modifying same collection while traversing using for each. If you want to modified same collection you need to use iterator.

val tempList = arrayListOf<MyObject>()
tempList.addAll(currentList)
currentList.forEach {
if (condition to remove here) {
tempList.remove(it)
}
}
currentList = tempList

Related

Progressbar not updating after Redux Event

I know this question has been asked quite often here, but non of the answers helped me.
I am writting a gallery app with a thumbes-regeneration feature. In oder to show the progress i added the progressbar which should count the number of created thumbnails. After each finished thumbnail-generation i dispatch a Redux event and listen to it in my Fragement, in order to change the progressbar.
Generating all thumbnails for all visible photos/videos
private fun onMenuRefreshThumbs(activity: Activity) {
val mediaPath = Redux.store.currentState.mediaPath
val fileRepository = FileRepository(context = activity, mediaPath = mediaPath)
activity.runOnUiThread {
fileRepository.regenerateThumbs(activity)
}
}
Functions inside the above used FileRepository:
fun regenerateThumbs(context: Context) {
val success = File(getAbsoluteThumbsDir(context, mediaPath)).deleteRecursively()
getMediaItems()
}
fun getMediaItems(): MediaItemList {
val success = File(thumbPath).mkdirs()
val isThumbsEmpty = File(thumbPath).listFiles().isEmpty()
val mediaFileList = File(mediaPath).listFiles().
.sortedByDescending { it.lastModified() }
val list = MediaItemList()
mediaFileList.apply {
forEach {
list.add(MediaItem(it.name, 0, 0))
if (isThumbsEmpty) {
getOrCreateThumb(it)
Redux.store.dispatch(FileUpdateAction(it))
}
}
}
return list
}
Subscribing to Redux in the Fragement:
private fun subscribeRedux() {
val handler = Handler(Looper.getMainLooper())
val activity = requireActivity()
subscriber = { state: AppState ->
when (state.action) {
...
is ClearSelection -> {
progressCounter = 0
// fragment_gallery_progress.visibility = View.GONE
}
is FileUpdateAction -> {
Handler().post {
progressCounter++
fragment_gallery_progress.visibility = View.VISIBLE
fragment_gallery_progress.progress = progressCounter
// fragment_gallery_progress.invalidate()
log.d("test: Thumb Index $progressCounter ${state.action.mediaItem.name} was created")
}
Unit
}
}
}.apply {
Redux.store.subscribe(this)
}
}
I tried all difference version of calling a thread in both cases. But no matter if its done with the handler or by activity.runOnUiThread, the progressbar never changes untill all thumbs are finished and the progressbar jumps from 0 to the maximum number. I can see the logs which are written in the right time, but not the progressbar changing.
I could fix my problem with following steps:
Removing the runOnUiThread() call
private fun onMenuRefreshThumbs(activity: Activity) {
val mediaPath = Redux.store.currentState.mediaPath
val fileRepository = FileRepository(context = activity, mediaPath = mediaPath)
fileRepository.regenerateThumbs(activity)
}
Adding a thread for each Thumbs-Generation:
fun getMediaItems(): MediaItemList {
val success = File(thumbPath).mkdirs()
val isThumbsEmpty = File(thumbPath).listFiles().isEmpty()
val mediaFileList = File(mediaPath).listFiles().
.sortedByDescending { it.lastModified() }
val list = MediaItemList()
mediaFileList.apply {
forEach {
list.add(MediaItem(it.name, 0, 0))
if (isThumbsEmpty) {
Thread {
getOrCreateThumb(it)
Redux.store.dispatch(FileUpdateAction(it))
}.start()
}
}
...

Android Multi-row summation: Request for code shortening

I have a table with fifteen rows. Each row have three columns and a total column. I want to get the total per row, the grand total, and the overall average.
The user may not enter data for all rows, and the user may skip a row.
So the code checks if the user have entered data in one of three fields of each row.
If the row is blank, ignore it.
If some of the fields are filled-up, tell the user to fill up the rest of the row.
If all the fields in a row is filled up, sum all its fields and increment the divider.
I have only pasted the codes for Rows 1 & 2 for brevity, but it shows the gist of what I'm trying to achieve:
The code:
var a1 = 0
var a2 = 0
var total = 0
var divider = 0
// Row 1
if (b1p1.text.isNotEmpty() or b2p1.text.isNotEmpty() or b3p1.text.isNotEmpty()) {
var y = 0
listOf(b1p1, b2p1, b3p1).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p1, b2p1, b3p1).forEach {
a1 += it.text.toString().toInt()
}
total1.text = a1.toString()
total += a1
e2 = 1
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
// Row 2
if (b1p2.text.isNotEmpty() or b2p2.text.isNotEmpty() or b3p2.text.isNotEmpty()) {
var y = 0
listOf(b1p2, b2p2, b3p2).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p2, b2p2, b3p2).forEach {
a2 += it.text.toString().toInt()
}
total2.text = a2.toString()
total += a2
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
if (e2 == 1) {
grandTotalTextView.text = total.toString()
average = total.toDouble()/divider
val decimalFormatter = DecimalFormat("#,###.##")
averageTextView.text = decimalFormatter.format(average).toString()
cyeSingleton.anct3b = decimalFormatter.format(average).toString()
} else {
Toast.makeText(activity, "Error 2", Toast.LENGTH_SHORT).show()
}
The table:
This is the best I could come up with. Should there be no other suggestion, I will settle for this.
Thanks in advance!
**EDIT: Thanks to ** https://stackoverflow.com/users/3736955/jemshit-iskenderov
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
private fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>): TotalResult {
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews as List<EditText>, totalTextViews[index])
if(!rowResult.ignoreRow){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
Toast.makeText(
activity,
"$divider, $allTotal, $showError", Toast.LENGTH_SHORT)
.show()
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
private fun calculateRowResult(rowTextViews:List<EditText>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.text.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
fun main(){
val totalResult = calculateTotalResult(
allTextViews = listOf(
listOf(t11,t12,t13),
listOf(t21,t22,t23)
),
totalTextViews = listOf(totalView1, totalView2)
)
// single Toast error
if(totalResult.showError){
// showToast(error)
}
// use totalResult.divider, totalResult.allTotal
}
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>){
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews, totalTextViews[index])
if(!rowResult.ignore){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
fun calculateRowResult(rowTextViews:List<TextView>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
Extracted calculateTotalResult() and calculateRowResult() so multiple rows and columns do not need to repeat same code.
calculateRowResult() processes singlet row of TextViews. I had to iterate rowTextViews twice, one to calculate ignore, the other to show error on TextView if not ignore. We don't show Toast Error here yet.
calculateTotalResult() iterates through all rows and gets total result. We show only one Toast Error (if required) after this step.
Code is pseudo-code, not tested.

Sorting Strings that contains number in kotlin

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
}

How to convert List<List<X>> to List<X> in RxAndroid?

I have list of ids and I am maping it to list of sublists so getting list> and then I need to get one list of items but I don't know the proper operator for it.
.map { it ->
val step = 200
var count = it.stores.size
val subLists = ArrayList<List<Int>>()
var i = 1
while (count > 0) {
val start = (i - 1) * step
//if...
var end = i * step
count -= step
val items = it.items.subList(start, end)
subLists.add(items)
i++
}
subLists
}
.toFlowable()
.flatMapIterable { it -> it }
.flatMap {
personDao.loadById(it)
}.toList()
.I need to get one list of items , how?
You can just simply use:
val oldList: List<List<T>> = listOf()
val newList = oldList.flatMap { it }
And you don't have to use Rx for it
If you need Rx:
val flowable = Flowable.fromIterable(oldList).flatMap {
Flowable.fromIterable(it)
}
And you get Flowable<T>
I want to answer my question which I haven't thought well .so my answer use map and inside map go over list and it to list of Items
.map {
var items = emptyList<Item>()
it.map {
items += it
}
items
}.toFlowable()

Remove data from list while iterating kotlin

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.

Categories

Resources