Kotlin :How to remove item stored in Array in shared preference - android

I want to remove an item (id = "productId") from an Array (key) stored in sharedPreferences.
I write a Utility class that contains all these functions below, but the item still exist in the Array stored, how could i change my code to reach my goal.
fun removeArrayDataByKeyValue(key: String, productId: String) {
val prod = getDataInArrayList(key)
val removeProduct = ProductData(
id = productId)
prod.remove(removeProduct)
if (prod != null) {
setDataInArrayList(prod, key)
}
}
fun setDataInArrayList(DataArrayList: ArrayList<ProductData>, key: String) {
val jsonString = Gson().toJson(DataArrayList)
sharedPreferences.edit().putString(key, jsonString).apply()
}
fun getDataInArrayList(key: String): ArrayList<ProductData> {
val emptyList = Gson().toJson(ArrayList<ProductData>())
return Gson().fromJson(
sharedPreferences.getString(key, emptyList),
object : TypeToken<ArrayList<ProductData>>() {
}.type
)
}
The following code in my activity :
Utility(this).removeArrayDataByKeyValue("FavoritesProducts", productFavoris.id.toString())
Toast.makeText(this, "Removed from favorites", Toast.LENGTH_LONG).show()
buttonAddToFavorite.setColorFilter(Color.parseColor("#FF000000"))
finish()
startActivity(intent)

Your code doesn't work because ArrayList's remove() method checks for equality to find the object you want to remove. You should override equals() method in your ProductData class because if you don't, equals() default is to check for identity :
class A(val id: Int)
val a1 = A(1)
val a2 = A(1)
a1 == a2 // false, they are different objects
Even better, make ProductData a data class (which generates proper equals and hashCode methods).
data class A(val id: Int)
val a1 = A(1)
val a2 = A(1)
a1 == a2 // true
I'll give you some more suggestions:
don't instantiate Gson each time: if you're always using the default configuration, reuse the instance. You can create a top-level property like defaultGson = Gson(). This applies to emptyList as well.
avoid using directly ArrayList unless you have a good reason: use the more generic List and MutableList.
remember to use lowercase names for arguments: DataArrayList should be dataArrayList
if you want to listen to SharedPreference changes to update your Activity, you can use sharedPreferences.registerOnSharedPreferenceChangeListener(). A better approach would be to use a ViewModel with LiveData, but I assume you are still learning, so I'll leave this advanced topic for later :)

Related

Loop-ing mutiple values with the use of 1 instance from a data class

I am trying to figure out how to loop a data class. I have a function called getAges() which contains a listof Ages from 1 - 10. Each age are called from a data class called Age, which should be an Int. How can I successfully loop through Age with different numbers, for ex 1-10? Appreciate the feedback!
My Data class:
#Entity(tableName = "dropdown_age")
data class Age(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "age")
val age: Int?
)
My Function called getAges:
class ProfileViewModel: Viewmodel() {
fun getAges() = listOf(
Age(1), Age(2), Age(3), Age(4), Age(5),
Age(6), Age(7), Age(8), Age(9), Age(10),
)
}
There is a List "constructor" function that can be used to create a List using a lambda where the lambda parameter is an index, starting at 0. (I use quotation marks for constructor because interfaces don't have true constructors. This is just a function that looks like a constructor because of how it is capitalized.)
fun getAges() = List(10) { Age(it + 1) }
Or you can use the map function with a range. map modifies each item out of any Iterable to produce a new List.
fun getAges() = (1..10).map { Age(it) }
// or
fun getAges() = (1..10).map(::Age)
Using collections api forEach
getAges().forEach{
println(it.age)
}
Or using normal For loop
for (age: Age in getAges()) {
println(age.age)
}

Reference an object in a class by using a string?

I want to reference an object within this class I have below:
class HerbData {
object Dill {
const val herbName: String = "This is Dill!"
const val scientificName: String = "Anethum Graveolens"
val dullThumbnail: Int = R.drawable.dill_thumbnail_attr
}
object Peppermint {
val herbName: String = "This is Peppermint!"
}
}
Is there anyway that I can reference the object by using a string in Kotlin? Here is somewhat what I mean:
HerbData."Dill".herbname
I can't find anything on this topic for Kotlin.
Another way you could do this is with an enum class. The advantage over a map is that you have a data structure you can reference directly in code, so you could use HerbData.Dill as well as HerbData["Dill"]. And that will enable you to take advantage of compile-time checking and lint warnings, refactoring, exhaustive pattern matching, code completion etc, because the data is defined in your code
enum class HerbData(
val herbName: String,
val scientificName: String? = null,
val dullThumbnail: Int? = null
) {
Dill("This is Dill!", "Anethum Graveolens", R.drawable.dill_thumbnail_attr),
Peppermint("This is Peppermint!");
companion object {
operator fun get(name: String): HerbData? =
try { valueOf(name) } catch(e: IllegalArgumentException) { null }
}
}
fun main() {
// no guarantee these lookups exist, need to null-check them
HerbData["Peppermint"]?.herbName.run(::println)
// case-sensitive so this fails
HerbData["peppermint"]?.herbName.run(::println)
// this name is defined in the type system though! No checking required
HerbData.Peppermint.herbName.run(::println)
}
>> This is Peppermint!
null
This is Peppermint!
Enum classes have that valueOf(String) method that lets you look up a constant by name, but it throws an exception if nothing matches. I added it as a get operator function on the class, so you can use the typical getter access like a map (e.g. HerbData["Dill"]). As an alternative, you could do something a bit neater:
companion object {
// storing all the enum constants for lookups
private val values = values()
operator fun get(name: String): HerbData? =
values.find() { it.name.equals(name, ignoreCase = true) }
}
You could tweak the efficiency on this (I'm just storing the result of values() since that call creates a new array each time) but it's pretty simple - you're just storing all the enum entries and creating a lookup based on the name. That lets you be a little smarter if you need to, like making the lookup case-insensitive (which may or may not be a good thing, depending on why you're doing this)
The advantage here is that you're generating the lookup automatically - if you ever refactor the name of an enum constant, the string label will always match it (which you can get from the enum constant itself using its name property). Any "Dill" strings in your code will stay as "Dill" of course - that's the limitation of using hardcoded string lookups
The question really is, why do you want to do this? If it's pure data where no items need to be explicitly referenced in code, and it's all looked up at runtime, you should probably use a data class and a map, or something along those lines. If you do need to reference them as objects within the code at compile time (and trying to use HerbData."Dill".herbName implies you do) then an enum is a fairly easy way to let you do both
Declare a Data Class
data class HerbData (
val scientificName: String,
val dullThumbnail: Int
)
Initialize a muteable map and put data in it
val herbData = mutableMapOf<String, HerbData>()
herbData.put("Dill", HerbData("Anethum Graveolens", R.drawable.dill_thumbnail_attr))
herbData.put("Peppermint", HerbData("Mentha piperita", R.drawable.peppermint_thumbnail_attr))
You can now just
herbData["Dill"]?.scientificName
class HerbData {
interface Herb {
val herbName: String
val scientificName: String
}
object Dill : Herb {
override val herbName: String = "This is Dill!"
override val scientificName: String = "Anethum Graveolens"
}
object Peppermint: Herb {
override val herbName: String = "This is Peppermint!"
override val scientificName: String = "Mentha piperita"
}
companion object {
operator fun get(name: String): Herb? {
return HerbData::class
.nestedClasses
.find { it.simpleName == name }
?.objectInstance as? Herb
}
}
}
println(HerbData["Dill"]?.herbName) // Prints: This is Dill!
println(HerbData["Peppermint"]?.scientificName) // Prints: Mentha piperita
println(HerbData["Pepper"]?.herbName) // Prints: null

Whats the Kotlin Syntax called where you have two names after var or val: var (name1, name2) =

I downloaded a project and I'm not really sure what exactly the following line does:
val (episode, setEpisode) = remember { mutableStateOf<EpisodeDetail?>(null) }
The only thing I don't get is why there are two names after the "val" word.
I tried to google for it but I really don't know the name of the syntax.
It's called a Destructuring Declaration
https://kotlinlang.org/docs/destructuring-declarations.html
You may have seen something similar in JavaScript when you have an object, and you can extract the keys to variables with the following
const { key1, key2 } = { key1:"first", key2:"second", ignored:"third" };
console.log(key1, key2) // first second
If you have a data class Kotlin will create the component<N> functions for you.
class MyClass (val myStr: String, val myInt: Int, val myBool: Boolean) {
operator fun component1(): String = myStr
operator fun component2(): Int = myInt
operator fun component3(): Boolean = myBool
}
fun main() {
val x = MyClass("Hello", 5, false)
val (y, _, z) = x // use _ to ignore values you don't need
println(y) // Hello
println(z) // false
}
Unlike Javascript which uses the key names, Kotlin data classes use field ordering (by defining your own component<N> functions you could swap the order of destructuring).

get the value of a parameter from a data class in kotlin

So, i'm pretty new to kotlin and still learning stuff, I have a data class named Country with 4 parameters
County(name:String, policePhone:String, ambulancePhone:String,
firebrigadePhone:String)
, a listOf Country with 27 objects in it and a var nameC1 taken from the MainActivity.
I've called the list method forEach and I want to confront every name in the list with the variable nameC and when a match is found execute some code.
data class Country(val name: String, val police:String, val ambulance:String,val firefighter:String) {
}
var nameC1 = (activity as MainActivity).nameC
val numberList= listOf<Country>(
Country("Austria","133","144","122"),
Country("Belgium","101","100","100"),
Country("Bulgaria","166","150","160"),
Country("Croatia","192","194","193"),
Country("Cyprus","199","199","199"),
Country("Czech Republic","158","155","150"),
Country("Denmark","112","112","112"),
Country("Estonia","112","112","112"),
Country("Finland","112","112","112"),
Country("France","17","15","18"),
Country("Germany","110","112","112"),
Country("Greece","100","166","199"),
Country("Hungary","107","104","105"),
Country("Ireland","112","112","112"),
Country("Italy","113","118","115"),
Country("Latvia","112","112","112"),
Country("Lithuania","02","03","01"),
Country("Luxembourg","113","112","112"),
Country("Malta","112","112","112"),
Country("Netherlands","112","112","112"),
Country("Poland","997","999","998"),
Country("Portugal","112","112","112"),
Country("Romania","112","112","112"),
Country("Slovakia","158","155","150"),
Country("Slovenia","113","112","112"),
Country("Spain","092","061","080"),
Country("Sweden","112","112","112")
)
numberList.forEach { if (Country.name==nameC1 ) }
// i'm expecting String1==String2 but i'm
//stuck here because it says name is an unresolved reference
}
I'd use a getName() but i know in kotlin getter/setter are automated ( I'm not used to it) and ihaven't found anything useful on the kotlin doc. site,
I've seen on this site that someone suggested to implement Kotlin-reflection but I don't understand how I'm not supposed to get a parameter from a class by default.
forEach creates a lambda for each of the element in the collection. The default name for the element inside the lambda is it. But you can rename it to something else too. Refer to the doc
Here is a working example of your code
data class Country(val name: String, val police:String, val ambulance:String,val firefighter:String)
fun doThis(nameC1: String) {
val numberList= listOf<Country>(
Country("Austria","133","144","122"),
Country("Belgium","101","100","100"),
Country("Bulgaria","166","150","160"),
Country("Croatia","192","194","193"),
Country("Cyprus","199","199","199"),
Country("Czech Republic","158","155","150"),
Country("Denmark","112","112","112"),
Country("Estonia","112","112","112"),
Country("Finland","112","112","112"),
Country("France","17","15","18"),
Country("Germany","110","112","112"),
Country("Greece","100","166","199"),
Country("Hungary","107","104","105"),
Country("Ireland","112","112","112"),
Country("Italy","113","118","115"),
Country("Latvia","112","112","112"),
Country("Lithuania","02","03","01"),
Country("Luxembourg","113","112","112"),
Country("Malta","112","112","112"),
Country("Netherlands","112","112","112"),
Country("Poland","997","999","998"),
Country("Portugal","112","112","112"),
Country("Romania","112","112","112"),
Country("Slovakia","158","155","150"),
Country("Slovenia","113","112","112"),
Country("Spain","092","061","080"),
Country("Sweden","112","112","112") )
numberList.forEach {
if (it.name == nameC1) {
println("Match")
}
}
}
fun main() {
doThis("Slovenia")
}
Try it for yourself on play.kotlinlang.org - Link
The above code will execute the println function when the condition is true.
In the forEach loop you have to use it to access the name parameter.
like this
numberList.forEach { if (it.name==nameC1 )}
Try with the following code. You can apply filter on list
//if you want iterate your list try with below code
numberList.forEach {
val name = it.name
val police = it.police
}
//If you want apply filter on list take reference from below code
private var countryList: ArrayList<Country> = arrayListOf(
Country("Austria", "133", "144", "122"),
Country("Belgium", "101", "100", "100")
)
val searchList = countryList.filter { country-> country.name == nameC1}

How to programically trigger notify on MutableLiveData change

I have a LiveData property for login form state like this
private val _authFormState = MutableLiveData<AuthFormState>(AuthFormState())
val authFormState: LiveData<AuthFormState>
get() =_authFormState
The AuthFormState data class has child data objects for each field
data class AuthFormState (
var email: FieldState = FieldState(),
var password: FieldState = FieldState()
)
and the FieldState class looks like so
data class FieldState(
var error: Int? = null,
var isValid: Boolean = false
)
When user types in some value into a field the respective FieldState object gets updated and assigned to the parent AuthFormState object
fun validateEmail(text: String) {
_authFormState.value!!.email = //validation result
}
The problem is that the authFormState observer is not notified in this case.
Is it possible to trigger the notification programically?
Maybe you can do:
fun validateEmail(text: String) {
val newO = _authFormState.value!!
newO.email = //validation result
_authFormState.setValue(newO)
}
You have to set the value to itself, like this: _authFormState.value = _authFormState.value to trigger the refresh. You could write an extension method to make this cleaner:
fun <T> MutableLiveData<T>.notifyValueModified() {
value = value
}
For such a simple data class, I would recommend immutability to avoid issues like this altogether (replaces all those vars with vals). Replace validateEmail() with something like this:
fun validateEmail(email: String) = //some modified version of email
When validating fields, you can construct a new data object and set it to the live data.
fun validateFields() = _authFormState.value?.let {
_authFormState.value = AuthFormState(
validateEmail(it.email),
validatePassword(it.password)
)
}

Categories

Resources