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
Related
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)
]
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 .
I have a MutableList in my Android project where i'm adding an object called Articolo, then when a new item is added to that list i need to check if one item with same ID exist and if it does i need to update it's quantity.
The issue is that i'm trying to use MutableList.find to find the object with the same ID and when i find it i'm simply add the quantity to existing quantity but instead it remains immutable.
Here is my Articolo.kt
data class Articolo(var barcode: String, var qta: Int) {
constructor() : this ("", 0)
}
And here is my function where i'm adding data to MutableList
private var articoli = mutableListOf<Articolo>()
private fun addBarcode(barcode: String, qta: Int) {
if (barcode.isEmpty()) {
txtBarcode.requestFocus()
return;
}
articoli.find{
it.barcode == barcode
}?.qta?.plus(qta) ?:
articoli.add(Articolo(barcode, qta))
}
So if i add the first object like barcode: 1111, qty: 1 and then another same object instead of having one element array with qty 2 i still have qty 1..
That's because .plus(Int) returns a new value. You're not changing the property.
Instead you should do:
fun addBarcode(barcode: String, qta: Int) {
val existant = articoli.find { it.barcode == barcode }
if (existant != null) existant.qta += qta
else articoli.add(Articolo(barcode, qta))
}
#VaiTon86 has the answer (you're not actually changing the value in the Articolo object) but really, you should probably be using a Map here anyway:
maximum one of each item
lookup by some value (barcode)
that's a map!
There's a few ways you could implement it, here's one:
val articoli = mutableMapOf<String, Articolo>()
private fun addBarcode(barcode: String, qta: Int) {
articoli.getOrPut(barcode) { Articolo(barcode, 0) }
.let { it.qta += qta }
}
So the getOrPut just adds a new zero-quantity Articolo entry if there isn't already one, and then you add qta to what's already there for that entry.
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)
}
I have a dataClass, which contains a unique code of item, code of the parent and two lists - categories and subcategories.
data class MyItem (
var code: String,
var name: String,*
var data: String,
var count: Int,
var parent: String,
var categories: MutableList<MyItem>,
var subcategories: MutableList<MyItem>
)
I've got from server 3 different items list. And structure that I want to get is:
- listOfTopLevelItems
--- listOfMiddleLevelItems
----- listOfBottomLevelItems
where every topLevelItem contains a list of middleLevelItems and every middle level items contains a list of bottom level items. For that i used code below
for (topItem in topLevelItems) {
for (middleItem in middleLevelItems) {
if (topItem.code == middleItem.parent) {
val middleResultItem = middleItem
for (bottomItem in bottomLevelItems) {
if (middleItem.code == bottomItem.parent) {
middleResultItem.subcategories.add(bottomItem)
}
}
topItem.categories.add(middleResultItem)
}
}
result.add(topItem)
}
But the problem is if i will have a lots of items on bottom level, than it will be a lot of iterations. Is there is another way to solve this?
So what you have is a DAG of depth 3. I am going to make some other adjustments other than just solving your iteration problem.
First, I think the structure of your data classes is a bit redundant for describing a graph of objects. You do not need the category and subcategory fields in my opinion. Stripping out the irrelevant fields, this is what mine would look like:
data class MyItem(
var code: String,
var parent: String? = null,
var categories: MutableList<MyItem> = mutableListOf()
){
val subcategories: List<MyItem>
get() = categories.flatMap { it.categories }
}
A root/top item will be any item where the parent is null. And then its categories are its immediate children, and its sub categories are its grandchildren. I have provided a property here which will take care of grandchildren if you really want that accessor, and it means if you add something to a child, the parents grandchildren will be updated automatically :D.
Now for version 1 of creating the object graph. This keeps things in line with your apparent structure of knowing which ones are roots, children and grand children. But this is not needed as you will see in version 2.
fun main() {
val topItems = listOf(MyItem("1"), MyItem("2"))
val middleItems = listOf(MyItem("1_1", "1"), MyItem("1_2", "1"), MyItem("2_1", "2"))
val bottomItems = listOf(MyItem("1_1_1", "1_1"), MyItem("1_2_1", "1_2"), MyItem("2_1_1", "2_1"))
val topByID = topItems.map { it.code to it }.toMap()
val middleByID = middleItems.map { it.code to it }.toMap()
bottomItems.forEach { middleByID[it.parent]?.categories?.add(it) }
middleItems.forEach { topByID[it.parent]?.categories?.add(it) }
println(topItems)
println(topItems[0].subcategories)
}
But really, all you need to know for building an obect graph is parent child relationship, and they can all just be in a big collection. Then you can rebuild your object graph like this:
fun main() {
val topItems = listOf(MyItem("1", "*"), MyItem("2", "*"))
val middleItems = listOf(MyItem("1_1", "1"), MyItem("1_2", "1"), MyItem("2_1", "2"))
val bottomItems = listOf(MyItem("1_1_1", "1_1"), MyItem("1_2_1", "1_2"), MyItem("2_1_1", "2_1"))
val allItems = topItems + middleItems + bottomItems
val allItemsByID = allItems.map { it.code to it }.toMap()
allItems.forEach {
allItemsByID[it.parent]?.categories?.add(it)
}
println(topItems)
println(topItems[0].subcategories)
}
This is my favorite approach :D