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.
Related
I have a bunch of integers in an ArrayList. The values are either 1 or 0 at any given time. I have a unique string error message that I would like to display for each of these items if any of the values switch to 1. I have a single textView where I would like to display the current error message if there is one.
For some reason, I cannot understand how to do this. Is there a data structure that I can leverage to solve this problem efficiently?
You can write an anonymous subclass of ArrayList or LinkedList that overrides the set function to do some action whenever a value is changed:
val myList = object: ArrayList<Int>() {
override fun set(index: Int, element: Int): Int {
return super.set(index, element).also { previousElement ->
if (previousElement == 0 && element == 1) {
showErrorMessageForIndex(index)
}
}
}
}
// where showErrorMessageForIndex is a function that sets a message on your text view.
If you do this kind of thing in multiple places in your app, you can generalize it into a reusable class like this:
class MyArrayList<T>(
val onValueChange: (index: Int, oldValue: T, newValue: T)-> Unit
): ArrayList<T>() {
override fun set(index: Int, element: T): T {
return super.set(index, element).also { previousElement ->
if (previousElement != element) {
onValueChange(index, previousElement, element)
}
}
}
}
Then when you create the list, use:
val myList = MyArrayList<Int> { index, oldValue, newValue ->
if (oldValue == 0 && newValue == 1) {
showErrorMessageForIndex(index)
}
}
I have a multiple choice quiz with 4 choices per answer. In the ArrayList with the questions and choices, the correct answer is set to the index of the correct option. I want to shuffle the choices but am not sure how to identify the new index of the correct answer. Any thoughts?
Question object
object ConstantsAnalysis {
const val TOTAL_CORRECT: String = "total_correct"
const val TOTAL_OPP: String = "total_opp"
fun getQuestions3(): ArrayList<Questions3> {
val questionList = ArrayList<Questions3>()
val q1 = Questions3(1, null,
"On a graph, the horizontal line along which data are plotted is the _____",
"y axis", "x axis", "origin", "quadrant", 2, R.string.Jones_1995, null)
questionList.addAll(listOf(q1))
questionList.shuffle()
return questionList
}
}
Data class
data class Questions3(
val id: Int, val image: Int?, val question: String, val option1: String, val option2: String,
val option3: String, val option4: String, val correctAnswer: Int, val dialogBox: Int?, val dialogBox2: Int?)
Shuffle choices
val ansorder = arrayOf(question.option1, question.option2, question.option3, question.option4)
ansorder.shuffle()
radio_button1.text = ansorder[0]
radio_button2.text = ansorder[1]
radio_button3.text = ansorder[2]
radio_button4.text = ansorder[3]
Check answer choice
if (questions3!!.correctAnswer != mSelectedOptionPosition) {
//do x
}
Edit (Since correct answer is a string and the index changes after shuffling, answerView(questions3.correctAnswer, R.drawable.correct_option_border.
class QuestionsActivityAnalysis : AppCompatActivity(), View.OnClickListener {
private var mCurrentPosition:Int = 1
private var mQuestionsList:ArrayList<Questions3>? = null
private var mSelectedOptionPosition:Int = 0
private var mCorrectAnswers: Int = 0
private var mSelectedOptionText: String? = null
private fun shuffle() {
val question = mQuestionsList!![mCurrentPosition - 1]
val ansorder = arrayOf(question.option1, question.option2, question.option3, question.option4)
ansorder.shuffle()
radio_button1.text = ansorder[0]
radio_button2.text = ansorder[1]
radio_button3.text = ansorder[2]
radio_button4.text = ansorder[3]
}
override fun onClick(v: View?) {
when(v?.id){
R.id.radio_button1 -> { selectedOptionView(radio_button1, 1)
mSelectedOptionText = radio_button1.text as String?
}
R.id.radio_button2 -> { selectedOptionView(radio_button2, 2)
mSelectedOptionText = radio_button2.text as String?
}
R.id.radio_button3 -> { selectedOptionView(radio_button3, 3)
mSelectedOptionText = radio_button3.text as String?
}
R.id.radio_button4 -> { selectedOptionView(radio_button4, 4)
mSelectedOptionText = radio_button4.text as String?
}
R.id.btn_submit -> {
val questions3 = mQuestionsList?.get(mCurrentPosition - 1)
if (questions3!!.correctAnswer != mSelectedOptionText) {
} else {
mCorrectAnswers++
}
answerView(questions3.correctAnswer, R.drawable.correct_option_border)
private fun answerView(answer: Int, drawableView: Int) {
when(answer){
1 -> {
radio_button1.background = ContextCompat.getDrawable(this, drawableView)
}
2 -> {
radio_button2.background = ContextCompat.getDrawable(this, drawableView)
}
3 -> {
radio_button3.background = ContextCompat.getDrawable(this, drawableView)
}
4 -> {
radio_button4.background = ContextCompat.getDrawable(this, drawableView)
}
}
}
I would really recommend just creating a data class like this:
data class QuestionOption(val question:String, val isCorrect = false)
Afterwards you can shuffle any way you like and just check if the selected QuestionOption has isCorrect set to true. You get a bunch of benefits and the logic gets simpler.
Edit:
To make it easier to declare questions this way:
In general if you add questions in your code you want only as much necessary code as required. For this you can either declare a good constructor or a function that basically maps your values to a constructor. In your case I'd say
data class Questions3(
val id: Int, val question: String, val option1: String, val option2: String,
val option3: String, val correctOption: String, val image: Int?=null,val dialogBox1: Int?=null,val dialogBox2: Int?=null)
(notice how the optional parameters are last, you don't need to specify them as well thanks to them beeing null by default)
Makes sense, in theory you could also (not too clean but easy) just shuffle option 1-3 & correctOption and then just compare if the correctOption String matches the selected String.
Otherwise as I said, you can always create logic for mapping stuff. Here you can either map from Constructor to another Constructor, same with Functions that return a finished Object.
I am trying to update my pojo class on a particular click in Kotlin but it is giving me error :-
java.lang.stackoverflowerror: stack size 8mb
Here is my Pojo Class
class NavDrawerItem(var icon_normal: Int,var icon_notified: Int, var title: String, var isShowNotify: Boolean){
var title1: String = title
// get() = title // Calls the getter recursively
set(value)
{ title1 = value }
var image: Int = icon_normal
// get() = image
set(value)
{ image = value }
var image_notified: Int = icon_notified
// get() = image
set(value)
{ image_notified = value }
var notify: Boolean = isShowNotify
set(value) {
notify = value
}
}
I am updating my Pojo on the Item click of NavigationDrawer
override fun onItemClick(position: Int) {
mDrawerLayout?.closeDrawer(this!!.containerView!!)
position1 = position
for (i in navDrawerItems.indices) {
val item = navDrawerItems[i]
item.notify=(if (i == position) true else false)
navDrawerItems[i] = item
mAdapter?.notifyDataSetChanged()
}
}
Please help me!!!!
Your setters create infinite loop, which causes the StackOverflowError exceptions.
class NavDrawerItem(var icon_normal: Int,var icon_notified: Int, var title: String, var isShowNotify: Boolean){
var title1: String = title
// get() = title // Calls the getter recursively
set(value)
{ field = value }
var image: Int = icon_normal
// get() = image
set(value)
{ field = value }
var image_notified: Int = icon_notified
// get() = image
set(value)
{ field = value }
var notify: Boolean = isShowNotify
set(value) {
field = value
}
}
The above is setting the field, where your implementation was recursively setting the values.
Also, as ADM mentioned it's better to move the notifyDataSetChanged outside of the loop and not updating at each iteration.
Modify your class as a simple data class.
data class NavDrawerItem(var icon_normal: Int,var icon_notified: Int, var title: String, var isShowNotify: Boolean)
And
override fun onItemClick(position: Int) {
mDrawerLayout?.closeDrawer(this!!.containerView!!)
for (i in navDrawerItems.indices) {
val item = navDrawerItems[i]
item.notify=(i == position)
}
mAdapter?.notifyDataSetChanged()
}
It is always recommended to use data classes for defining pojos.
Because data classes are only made for storing data.
They provides many unique features over normal class in kotlin.
for example, you don't need to define setter and getters, they are automatically added to your data class.
In addition, your data class will automatically override some useful functions like equals, hashCode, toString, and etc.
defining data class is very easy.
data class Foo ( val title : String, val isHungry : Boolean ){
}
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
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