Improving Time Efficiency on two custom List in KOTLIN - android

There are two lists:
val listA:List<Amodel>
val listB:List<Int>
data class Amodel(val id:Int,var isUsed:Boolean=false)
Need to update listA isUsed =true based on the int id of the listB
What I'm doing now:
listB.foreach(){
//here i'm updating but it's taking too much time w.r.t time efficiency.
}
any GOOD solution.
Note list size is 500k+ for simulation

This might be slightly faster when the lists are huge:
val lookup = listB.associateWith { true }
listA.forEach { it.isUsed = lookup[it.id] ?: false }
Possibly this is even faster, I'm not sure. Because it then only sets isUsed in the case it needs to be true:
val lookup = listB.associateWith { true }
listA.forEach { lookup[it.id]?.run { it.isUsed = true } }

data class Amodel(val id: Int, var isUsed: Boolean = false)
val listA: List<Amodel> = listOf(
Amodel(1),
Amodel(2),
Amodel(3),
Amodel(4),
Amodel(5)
)
val listB: List<Int> = listOf(2, 4, 5)
listA.forEach { if (it.id in listB) it.isUsed = true }
// Alternative to set all items, not only from false to true:
listA.forEach { it.isUsed = it.id in listB }
println(listA)
Output:
[
Amodel(id=1, isUsed=false),
Amodel(id=2, isUsed=true),
Amodel(id=3, isUsed=false),
Amodel(id=4, isUsed=true),
Amodel(id=5, isUsed=true)
]

How about our good buddy Set?
val usedIds = listB.toSet()
listA.forEach { it.isUsed = it.id in usedIds }
If you just need to know if a thing is present in a collection, that's basically what a set's for! Might shave a bit of time off vs a map since you don't need to store any value data

data class Amodel(val id:Int,var isUsed:Boolean=false)
val listA = listOf(
Amodel(3),
Amodel(2),
Amodel(3),
Amodel(8),
Amodel(0)
)
val listB = listOf(2, 0, 5)
listA.forEach { aModel ->
// here we check listB have element of listA or not
if (listB.contains(aModel.id)){
aModel.isUsed = true
}
}
println(listA)
**RESULT:-**
[
Amodel(id=3, isUsed=false),
Amodel(id=2, isUsed=true),
Amodel(id=3, isUsed=false),
Amodel(id=8, isUsed=false),
Amodel(id=0, isUsed=true)
]

Related

Kotlin multiple when statement

I am learning to build a simple android app with android studio and i created a function to find the id of some values. While writing this function I thought using when statement (Kotlin) but a sadly had to repeat it. Is there a way to assign the result of a when statement to multiple variables at the same time? In other language I would just have returned a list which I would have disassembled but I can't find a way to do it in Kotlin. It's not really big problem but I like optimizing my code.
// my Kotlin function
// setting a specific state
private fun setState(num: Int) {
Log.v(TAG, num.toString())
// get the correct image id
val imageId: Int? = when (num) {
0 -> R.drawable.lemon_restart
1 -> R.drawable.lemon_tree
2 -> R.drawable.lemon_squeeze
3 -> R.drawable.lemon_drink
else -> null
}
// get the correct text to show
val txtId: Int? = when (num) {
0 -> R.string.txt_state_0
1 -> R.string.txt_state_1
2 -> R.string.txt_state_2
3 -> R.string.txt_state_3
else -> null
}
// get the correct content description for accessibility
val contentDescriptionId: Int? = when (num) {
0 -> R.string.lemon_restart_description
1 -> R.string.lemon_tree_description
2 -> R.string.lemon_squeeze_description
3 -> R.string.lemon_drink_description
else -> null
}
// setting the new stuff
val imView: ImageView = findViewById(R.id.imageState)
val txtView: TextView = findViewById(R.id.textOrder)
txtView.text = getString(txtId!!)
imView.setImageResource(imageId!!)
imView.contentDescription = getString(contentDescriptionId!!)
}
feel free to optimize it as much as possible
You can return Triple or your own data class from when, and then destructure it:
val (imageId, txtId, contentDescriptionId) = when (num) {
0 -> Triple(R.drawable.lemon_restart, R.string.txt_state_0, R.string.lemon_restart_description)
...
else -> Triple(null, null, null)
}
Since every field is constant and states are fixed. you can make the states as constant. to decouple code little bit you can create a separate class to return the values for particular state. below is an Example :
class StateHandle private constructor(imageId: Int?, txtId: Int?, contentDescriptionId: Int?) {
companion object {
private val imageIds = arrayOf(
R.drawable.lemon_restart,
R.drawable.lemon_tree,
R.drawable.lemon_squeeze,
R.drawable.lemon_drink
)
private val txtIds = arrayOf(
R.string.txt_state_0,
R.string.txt_state_1,
R.string.txt_state_2,
R.string.txt_state_3
)
private val contentIds = arrayOf(
R.string.lemon_restart_description,
R.string.lemon_tree_description,
R.string.lemon_squeeze_description,
R.string.lemon_drink_description
)
#JvmStatic
fun getStateFor(num: Int): StateHandle {
return StateHandle(
imageIds.getOrNull(num), txtIds.getOrNull(num),
imageIds.getOrNull(num)
)
}
}
}
Its not perfect but it is a bit more reusable . just call #getStateFor and use the StateHandle object .

Remove item from mutable list added from a model in kotlin

In my code, I create a mutable list and add elements from a model:
var lista: MutableList<ExpenseItem> = mutableListOf()
...
class ExpenseItem (val name: String, val word: String, val flavour: String)
...
val currentExpense = ExpenseItem("Sergio", "Aguacate", "Duro")
val currentExpense1 = ExpenseItem("amaya", "fresas", "pan")
val currentExpense2 = ExpenseItem("emma", "limon", "agua")
lista.add(currentExpense)
lista.add(currentExpense1)
lista.add(currentExpense2)
Now I am looking for a way to remove elements knowing, for example, the ´name´ field
I have tried the filters, remove, drop, etc for the list. I've also tried "when", but I think I'm not finding the correct syntax or way to do it,
I really appreciate the help.
It sounds like the method you want is
lista.removeAll { it.name == nameToRemove }
If you intend to modiify the actual list, then you'll want removeAll.
lista.removeAll {
it.name == "nameToRemove"
}
If you don't want to modify the original list, then filter can you get a new list without those elements.
val newList = lista.filter{
it.name != "nameToRemove"
}
Below shows a complete explanation of the behavior
var list: MutableList<String> = mutableListOf("1","2", "3")
//Shows all items
list.forEach {
println(it)
}
//Makes a new list with all items that are not equal to 1
val newList = list.filter {
it != "1"
}
newList.forEach {
println(it)
}
//Original list is untouched
list.forEach {
println(it)
}
//Modifies this list to remove all items that are 1
list.removeAll {
it == "1"
}
list.forEach {
println(it)
}

Change a value in mutable list in Kotlin

I got this mutablelist:
[Videos(id=4, yt_id=yRPUkDjwr1A, title=test4, likes=0, kat=pranks, ilike=false), Videos(id=3, yt_id=WkyUU9ZDUto, title=test3, likes=0, kat=pranks, ilike=false), Videos(id=2, yt_id=B_X9OQqtduE, title=test2, likes=0, kat=animals, ilike=false), Videos(id=1, yt_id=ywaKlGNiv80, title=test1, likes=0, kat=animals, ilike=false)]
How can I change ilike to true where id is 2
This is what I've tried:
for (i in 0 until vids!!.size) {
Log.d("lets", vids!!.get(i).title)
if(vids!!.get(i).id == 2){
vids!!.get(i).ilike = true
}
}
You can use find function to find the element with id = 2 and change its property:
vids?.find { it.id == 2 }?.iLike = true
Note: it is a good practice to use question mark if the property is nullable and you unsure whether it is null or not.
If you expect few items (maybe 1 or 2?) to be affected,
you can filter the list and then change iLike of the filtered items:
vids!!.filter { it.id == 2 }.forEach { it.iLike = true }
Try this, I'm assuming your Videos structure is a data class defined somewhat like so. data class Videos(val id: Int, val yt_id: String, val title: String, val likes: Int, val kat: String, val ilike: Boolean)
list.forEachIndexed { index, video ->
video.takeIf { it.id == 2}?.let {
list[index] = it.copy(ilike = true)
}
}
I had to change several properties and I had a need to hold the changed object. Therefore following approach worked better for me:
//First, find the position of the video in the list
val videoPosition= list.indexOfFirst {
it.id == 2
}
//Now get your video by position and make changes
val updatedVideo = list[videoPosition].apply {
//Make all changes you need here
ilike = true
//...
}
//Finally, replace updated video into your list.
list[videoPosition] = updatedVideo
Use set to replace the object if you don't want to use predicates or iteration
Eg.
val video = (...,read = true) //or however you are getting the current model
val updatedVideo = video
updatedVideo.read = true
vids[vids.indexOf(video)] = updatedVideo

Get uncommon elements from two list - KOTLIN

I have two list of same model class (STUDENT), sample student object structure is given below,
{
"_id": "5a66d78690429a1d897a91ed",
"division": "G",
"standard": "X",
"section": "Secondary",
"lastName": "Sawant",
"middleName": "Sandeep",
"firstName": "Shraddha",
"pin": 12345,
"isEditable": true,
"isTracked": false
}
One list have 3 objects and other 2. lets say, List A has 1, 2, 3 students and List B has 1, 2
So my question is there any inbuilt functions to get the uncommon element by comparing just the id? If not how can i solve this issue.
FYI, following are the two approaches i have made to solve, but failed miserably.
Approach 1.
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
val consolidated = prefStudents.filter {
prefStudents.any { students: Students -> it._id == students._id }
}
return prefStudents.minus(consolidated)
}
Approach 2.
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
val consolidatedStudents = studentsList + prefStudents
val distinctStudents = consolidatedStudents.distinctBy{ it._id }
return prefStudents.minus(distinctStudents)
}
Any kind of help will be greatly appreciated.
Thanks
A more Kotlin way to achieve what Ahmed Hegazy posted. The map will contain a list of elements, rather than a key and count.
Using HashMap and Kotlin built-ins. groupBy creates a Map with a key as defined in the Lambda (id in this case), and a List of the items (List for this scenario)
Then filtering out entries that have a list size other than 1.
And finally, converting it to a single List of Students (hence the flatMap call)
val list1 = listOf(Student("1", "name1"), Student("2", "name2"))
val list2 = listOf(Student("1", "name1"), Student("2", "name2"), Student("3", "name2"))
val sum = list1 + list2
return sum.groupBy { it.id }
.filter { it.value.size == 1 }
.flatMap { it.value }
I know that this is an old post but I believe there is a neater and shorter solution. See sample below using Mikezx6r's data whose answer was accepted above.
val list1 = listOf(Student("1", "name1"), Student("2", "name2"))
val list2 = listOf(Student("1", "name1"), Student("2", "name2"), Student("3", "name2"))
val difference = list2.toSet().minus(list1.toSet())
If you have two lists, where element is identified e.g. by some kind of id (item.id), then you can do as below:
fisrtList.filter { it.id !in secondList.map { item -> item.id } }
I assume firstList and secondList contain objects of the same type naturally.
Here's an extension function that basically does what you want. It makes an assumption that the element E knows how to be identified, e.g. by Student._id in your example:
infix fun <E> Collection<E>.symmetricDifference(other: Collection<E>): Set<E> {
val left = this subtract other
val right = other subtract this
return left union right
}
Here's an example of how it could be used:
val disjunctiveUnion: List<Student> = listA symmetricDifference listB
An example test case I'd written for it:
#Test
fun `symmetric difference with one of either set`() {
val left = listOf(1, 2, 3)
val right = listOf(2, 3, 4)
val result = left symmetricDifference right
assertEquals(setOf(1, 4), result)
}
This is the solution using a HashMap, the code could be better, but I'm very new to kotlin
fun getDistinctStudents(studentsList: List<Student>, prefStudents: List<Student>): List<Student> {
val studentsOccurrences = HashMap<Student, Int>()
val consolidatedStudents = studentsList + prefStudents
for (student in consolidatedStudents) {
val numberOfOccurrences = studentsOccurrences[student]
studentsOccurrences.put(student, if(numberOfOccurrences == null) 1 else numberOfOccurrences + 1)
}
return consolidatedStudents.filter { student -> studentsOccurrences[student] == 1 }
}
Your student class should be a data class or at least overrides hashcode and equals to be used as a key.
Until someone comes up with a neater and shorter solution, here's a working one that I think is easy enough to read:
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
val studentsIds = studentsList.map { it._id } // [ 1, 2, 3 ]
val prefStudentIds = prefStudents.map { it._id } // [ 1, 2 ]
val commonIds = studentsIds.intersect(prefStudentIds) // [ 1, 2 ]
val allStudents = studentsList + prefStudents // [ Student1, Student2, Student3, Student1, Student2 ]
return allStudents.filter { it._id !in commonIds } // [ Student3 ]
}
If you have a very large amount of students (hundreds), consider using sequences for the various steps, and perhaps filtering before concatenating the last two lists could help too:
val filteredStudents = studentsList.filter { it._id !in commonIds }
val filteredPrefStudents = prefStudents.filter { it._id !in commonIds }
return filteredStudents + filteredPrefStudents
Edit: see this answer instead.
Finally after some searching on Kotlin docs i have the solution. the function i was looking for was filterNot
Here is the complete solution which i tried.
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
return prefStudents.filterNot { prefStudent ->
studentsList.any {
prefStudent._id == it._id
}
}
}
Which returned the uncommon elements.
On mobile right now so I can’t test it but this might work for what you need.
Using subtract from stdlib https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/subtract.html
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents:
List<Students>): List<Students> {
return prefStudents.subtract(studentList) + studentList.subtract(prefStudents)
}

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