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}
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 :)
My MainActivity is a quiz where each question includes a betSize value and a potSize value. After the quiz ends I am sending 2 Double Arrays, betSizeArray and potSizeArray, from MainActivity to ResultsActivity to display in a ListView. I initialize and populate the arrays in the first activity and use putExtra() to put them in an intent. In ResultsActivity I am using intent.extras?.getDoubleArray(key) to retrieve each array; however the retrieved arrays are full of nulls.
In ResultsActivity.OnCreate() I first tried
val betSizeArray = intent.getDoubleArrayExtra("BET_SIZES").
This causes a NullPointerException whenever I tried to access a value from betSizeArray.
Next I tried
val betSizeArray = extras?.getDoubleArray("BET_SIZES").
Having to use safe call "?." to get the arrays and to access the array's values (such as betSizeArray?.get(i)) doesn't throw a NullPointerException; however all values accessed from the array are NULL.
Have tried initializing the array in MainActivity inside onCreate() instead, however that just caused NullPointerExceptions whenever using these arrays in MainActivity.
In the function in MainActivity where I create the intent and call startActivity(intent), I tried initializing and populating a testArrayList to make sure the array I was passing into the intent is populated and not full of nulls. However the array retrieved in ResultActivity is still full of nulls. I did not include this test in the following code.
var betSizeArray = arrayOfNulls<Double>(20)
var potSizeArray = arrayOfNulls<Double>(20)
var count = 0
var betSize = 1.0
var potSize = 1.0
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//here an OnKeyListener that eventually calls setNextQuestion() is created
....
}
setNextQuestion(), where the arrays get populated:
private fun setNextQuestion() {
//check if 20 questions have been completed
if (count < 19){
//values are added to each array
betSizeArray[count] = betSize
potSizeArray[count] = potSize
count++
...
/*new values are created for betSize and potSize for next time
this function is called*/
}
else endQuiz()
}
endQuiz() where the intent is created and startActivity() is called.
private fun endQuiz() {
val resultsIntent = Intent(applicationContext, ResultsActivity::class.java)
resultsIntent.putExtra("BET_SIZES", betSizeArray)
resultsIntent.putExtra("POT_SIZES", potSizeArray)
startActivity(resultsIntent)
}
ResultsActivity:
class ResultsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_results)
val extras = intent.extras
val betSizeArray = extras?.getDoubleArray("BET_SIZES")
val potSizeArray = extras?.getDoubleArray("POT_SIZES")
var listData = ArrayList<String>()
var textData = ""
//this loop is where the arrays are accessed.
for( i in 0 .. 19){
textData = "$" + betSizeArray?.get(i).toString() +
" into $" + potSizeArray?.get(i).toString()
listData.add(textData)
}
val arrayAdapter = ArrayAdapter(
this,
android.R.layout.simple_list_item_1,
listData
)
resultsListView.setAdapter(arrayAdapter)
assessTextView.setText(assessment)
}
}
The code successfully populates the ListView. However all values accessed from the arrays come back as "$null". Each line in this ListView reads as:
$null into $null
$null into $null
...
Output I would expect if everything went correctly would look something like:
$50 into $85
$15 into $40
...
where the values are random numbers generated in MainActivity and passed to ResultActivity via the arrays and the intent.
I still suspect that I need to initialize the arrays in onCreate() of MainActivity. But that just threw errors whenever adding values to the arrays in MainActivity.
Thanks for any help!
edit: #Azhar92 so I just tried in MainActivity where the intent is created:
val bundle = Bundle()
bundle.putSerializable("BET_SIZES", betSizeArray)
bundle.putSerializable("POT_SIZES", potSizeArray)
and in ResultActivity:
val bundle = intent.extras
val betSizeArray = bundle?.getSerializable("BET_SIZES") as ArrayList<Double>
val potSizeArray = bundle?.getSerializable("POT_SIZES") as ArrayList<Double>
and I'm getting a TypeCastException:
Caused by: kotlin.TypeCastException: null cannot be cast to non-null type kotlin.collections.ArrayList<kotlin.Double> /* = java.util.ArrayList<kotlin.Double> */
at com.example.questgen.ResultsActivity.onCreate(ResultsActivity.kt:39)
line 39 being the first getSerializable(). So it still seems the array that's getting passed through the intent is null for some reason.
val betSizeArray = bundle?.getSerializable("BET_SIZES") as Array<Double?>?
casting to Array? instead doesn't throw an error, but the array is still filled with NULL.
final edit: It's working! I moved all variables to under the class header. Changed array initialization from arrayOfNulls to DoubleArray(20). Took out a lot of private method parameters since I now have class variables that make sense and are accessible. Switched from bundle.putSerializable() to intent.putExtra(). In ResultsActivity I switched from bundle.getSerializable() to intent.getDoubleArrayExtra(). I assume there was some way to make Serializable and bundles work but this is what got me results for the first time. I think you're right about me re-declaring variables somewhere before. Thank you gmetal!
The getDoubleArrayExtra will return a Java double[] which may not include null values. Since you are initialise them with the arrayOfNulls method, an Array<Double?> will be returned. And when you retrieve it from the extras of an intent, you will need an Array<Double?>?, because a null value may be returned.
So you should read them back as Serializable values (all Java object representation of primitive types are Serializable) and cast them to Array<Double?>?:
val extras = intent.extras
val betSizeArray = extras?.getSerializable("BET_SIZES") as Array<Double?>?
val potSizeArray = extras?.getSerializable("POT_SIZES") as Array<Double?>?
Also note that the type of the betSizeArray on the ResultsActivity will be: Serializable?, because it may be null (e.g. not contained in the bundle), and must be cast to the desired Array? value.
If this doesn't work, this means that you are not doing something correct in your MainActivity. I would first move these variable declarations:
var betSizeArray = arrayOfNulls<Double>(20)
var potSizeArray = arrayOfNulls<Double>(20)
var count = 0
var betSize = 1.0
var potSize = 1.0
inside the MainActivity like this:
class MainActivity : AppCompatActivity() {
var betSizeArray = arrayOfNulls<Double>(20)
var potSizeArray = arrayOfNulls<Double>(20)
var count = 0
var betSize = 1.0
var potSize = 1.0
Updated- The code in the question works now
I'm trying to run a function after clicking on a button. The function updates an array and I want to run the next lines (The next lines transfer me to another activity) if the array isn't empty.
I tried to open the new activity within the filterPlaces function but with no success, startActivity and Intent don't work.
This is the function that updates the array:
var places = ArrayList<Place>() //Global place array
class MainActivity : AppCompatActivity() {
fun filterPlaces(types: ArrayList<String>, foods: ArrayList<String>, maxPrice: Int, maxProximity: Int) {
var typesList = types
val foodList = foods
if (types.isEmpty()) {
typesList = arrayListOf("Restaurant", "Hangouts")
if (foods.isEmpty()) {
foodList.add("Pizza")
}
}
val db = FirebaseFirestore.getInstance()
db.collection("places").get().addOnSuccessListener { result ->
for (document in result) {
val typeMatches = document.data["Type"].toString() in typesList
val foodMatches = document.data["Food"].toString() in foodList
var price = 0
when (document.data["Price"].toString()) {
"Very cheap" -> price = 0
"Cheap" -> price = 1
"Average" -> price = 2
"Far" -> price = 3
"Very far" -> price = 4
}
val priceMatches = price <= maxPrice
var proximity = 0
when (document.data["Proximity"].toString()) {
"Very close" -> proximity = 0
"Close" -> proximity = 1
"Far" -> proximity = 2
"Very far" -> proximity = 3
}
val proximityMatches = proximity <= maxProximity
if (typeMatches and foodMatches and priceMatches and proximityMatches) {
val place = Place(
document.data["Place"].toString(),
document.data["Type"].toString(),
document.data["Food"].toString(),
document.data["Price"].toString(),
document.data["Proximity"].toString()
)
places.add(place)
Log.d("name", "Place added successfully")
}
}
//Openning the results activity
if (places.isNotEmpty()) {
val i = Intent(this, RelevantPlaces::class.java)
val b = Bundle()
b.putParcelableArrayList("places", places)
i.putExtra("bundle", b)
startActivity(i)
}
}
.addOnFailureListener { exception ->
Log.d("name", "Error getting documents.")
}
}
This is the on click function:
fun onSortFilterClicked(view: View) {
if (places.isEmpty()) filterPlaces(types, foods, priceRange.progress, proximityRange.progress)
}
I want to run filterPlaces first, update the places array while I run it, and only than check if the array is still empty and if not open the new activity.
What actually happens is that it calls the filterPlaces but doesn't do it, instead it checks the places array (The if condition in the code) and only than goes into filterPlaces and run what's in it, resulted in me need to press twice on the button and only than the array has values.
I'm running this on Android Studio and I'm new at this Kotlin world and android developing in general.
Is there a solution for this? Either open the activity within the function or make the function run first?
What is happening?
An asynchronous request is made in filterPlaces, this is why the method itself returns immediately and passes control to the next code block.
How to fix this?
Move your code starting another Activity into the scope of your success listener. A better approach is to place this code into a separate method and just call it as needed.
Placed the filterPlace function outside the Main Activity class. Moved the function in the Main Activity class and it worked.