How to get the index of a gson object? - kotlin

I need to get the index of the array containing the member fileName = "Andres"
data class File(var fileName: String, var _id : String? = null)
data class Files(val files: Array<File>)
val miObjetG = Gson().fromJson(response_files, Files::class.java)
var indice = miObjetG.files.filterIndexed { index, file -> file.fileName == "Andres"}

I think indexOfFirst is what you are looking for:
val index = miObjetG.files.indexOfFirst{ it.fileName == "Andres" }

Related

Merge properties of a list to another based on properties objects

I got 2 lists with x objects inside , for example:
data class Model(
var token: String = "",
var id: String = "",
var name: String = "",
var image: Int = 0,
)
array is initialized and filled, the other list has x objects also that contains the objects of the first list but with different values in their properties!
what I want to do is to change the properties of the first array by the second one if they got the same object.name
var arr1 = ArrayList<Model>() // locale
var arr2 = ArrayList<Model>() // from db
the first array I got for example
[Model(name = "David", token = "" , image = 0)]
the second array I got
[Model(name = "David", token = "1asd5asdd851", image = 1)]
How do I make the first array take the missing token?
I tried with .filter{} and with .map{}. groupBy {} for hours because Name is the only properties that are the same but I'm more and more confused.
We can first group the second array by name using associateBy() and then iterate over first array and reassign properties:
val arr2ByName = arr2.associateBy { it.name }
arr1.forEach { item1 ->
arr2ByName[item1.name]?.let { item2 ->
item1.token = item2.token
item1.image = item2.image
}
}
Alternatively, if you don't need to modify items in arr1, but create another array and you can use items from both arr1 and arr2, then it will be much easier:
val arr3 = arr1.map { arr2ByName[it.name] ?: it }
One possible way would be to use fold() as follows:
fun main(args: Array<String>) {
val arr1 = listOf(Model(name = "David", token = "" , image = 0))
val arr2 = listOf(Model(name = "David", token = "1asd5asdd851", image = 1))
val mergedModels = arr2.fold(arr1) { localModels, dbModel ->
localModels.map { localModel ->
if (localModel.name == dbModel.name) localModel.copy(token = dbModel.token, image = dbModel.image)
else localModel
}
}
println(mergedModels)
}
If you want to reuse arr1 variable then you can do the following (but I would still use the previous option):
fun main(args: Array<String>) {
var arr1 = listOf(Model(name = "David", token = "" , image = 0))
val arr2 = listOf(Model(name = "David", token = "1asd5asdd851", image = 1))
arr1 = arr2.fold(arr1) { localModels, dbModel ->
localModels.map { localModel ->
if (localModel.name == dbModel.name) localModel.copy(token = dbModel.token, image = dbModel.image)
else localModel
}
}
println(arr1)
}

Compare multiple fields of Object to those in an ArrayList of Objects

I have created a 'SiteObject' which includes the following fields:
data class SiteObject(
//Site entry fields (10 fields)
var siteReference: String = "",
var siteAddress: String = "",
var sitePhoneNumber: String = "",
var siteEmail: String = "",
var invoiceAddress: String = "",
var invoicePhoneNumber: String = "",
var invoiceEmail: String = "",
var website: String = "",
var companyNumber: String = "",
var vatNumber: String = "",
)
I want to filter an ArrayList<SiteObject> (call it allSites) by checking if any of the fields of the objects within the list match those in a specific <SiteObject> (call it currentSite).
So for example, I know how to filter looking at one field:
fun checkIfExistingSite(currentSite: SiteObject) : ArrayList<SiteObject> {
var matchingSites = ArrayList<SiteObject>()
allSites.value?.filter { site ->
site.siteReference.contains(currentSite.siteReference)}?.let { matchingSites.addAll(it)
}
return matchingSites
}
But I am looking for an elegant way to create a list where I compare the matching fields in each of the objects in allSites with the corresponding fields in currentSite..
This will give me a list of sites that may be the same (allowing for differences in the way user inputs data) which I can present to the user to check.
Use equals property of Data Class:
val matchingSites: List<SiteObject> = allSites
.filterNotNull()
.filter { it.equals(currentSite) }
If you are looking for a more loose equlity criteria than the full match of all fields values, I would suggest usage of reflection (note that this approach could have performance penalties):
val memberProperties = SiteObject::class.memberProperties
val minMatchingProperties = 9 //or whatever number that makes sense in you case
val matchingItems = allSites.filter {
memberProperties.atLeast(minMatchingProperties) { property -> property.get(it) == property.get(currentSite) }
}
fun <E> Iterable<E>.atLeast(n: Int, predicate: (E) -> Boolean): Boolean {
val size = count()
return when {
n == 1 -> this.any(predicate)
n == size -> this.all(predicate)
n > size - n + 1 -> this.atLeast(size - n + 1) { !predicate.invoke(it) }
else -> {
var count = 0
for (element in this) {
if (predicate.invoke(element)) count++
if (count >= n) return true
}
return false
}
}
}
you could specify all the fields by which you want to match the currentSite inside the filter predicate:
fun checkIfExistingSite(currentSite: SiteObject) =
allSites.filter {
it.siteAddress == currentSite.siteAddress
|| it.sitePhoneNumber == currentSite.sitePhoneNumber
|| it.siteReference == currentSite.siteReference
}
Long but fast solution because of short circuiting.
If the list is nullable you can transform it to a non nullable list like:
allSites?filter{...}.orEmpty()
// or imho better
allSites.orEmpty().filter{...}

how to find an index in Arraylist of custom object based on its specific properties in Kotlin?

I have an arraylist of event
var approvedEvents = ArrayList<Event>()
the class of Event is like this
class Event() {
var eventID : String = ""
var createdBy: String = "" // uid of user creator
var creatorFullName: String = ""
var creatorIsVerified : Boolean = false
var creatorProfilePictureImagePath = ""
var createdAt : Date = Calendar.getInstance().time
var hasBeenApproved : Boolean = false
var title : String = ""
var speaker : String? = null
var coordinate : GeoPoint = City.defaultCityCoordinate
var address : String = ""
var city : String = ""
var venue : String = ""
}
so I want to find an index in approvedEvents arraylist that its eventID match selectedEvent.eventID how to do that in Kotlin ? is there specific method that I can use ?
Use indexOfFirst or indexOfLast to find the index of an item in an ArrayList based on your own criteria like below:
val index = approvedEvents.indexOfFirst{
it.eventID == selectedEvent.eventID
}
First of all, you have to override equals function in your Event class like
------
------
var city : String = ""
var venue : String = ""
override fun equals(other: Any?): Boolean{
if(other is Event){
return eventID.equals(other.eventID)
}
return false;
}
}
Now when you want to search for an Event object with eventId in a list, first create a temporary event object with that eventId which you want to search like
val temp=Event()
temp.eventID="102"
and simply get the index
print(events.indexOf(temp))

How to merge two different classes data into one in Kotlin

I have use two different classes:
ListTitle.kt
class ListTitle {
var id: Int? = null
var title: String? = null
constructor(id:Int, title: String) {
this.id = id
this.title = title
}
}
ListDes.kt
class ListDes {
var address: Int? = null
var des: String? = null
constructor(address: Int, des: String) {
this.address = address
this.des = des
}
}
listOfTitle and listDes are ArrayLists:
listOfTitle.add(ListTitle(1, "Hello"))
listOfTitle.add(ListTitle(2, "World"))
listDes.add(ListDes(1, "World Des"))
listDes.add(ListDes(2, "Hello Des"))
I want to assign title of ListTitle to des of ListDes by matching them by id/address for each element of the two lists.
How can I approach this?
You can use zip to merge two lists into one which has Pairs as elements.
val listOfTitle = listOf(ListTitle(1, "Hello"), ListTitle(2, "World"))
val listDes = listOf(ListDes(1, "World Des"), ListDes(2, "Hello Des"))
val pairList = listOfTitle.zip(listDes)
// since an element in the new list is a pair, we can use destructuring declaration
pairList.forEach { (title, des) ->
println("${title.title} ${des.des}")
}
Output:
Hello World Des
World Hello Des
A few notes:
You can write your classes in a shorter form in Kotlin. Just put the properties directly in the argument list of the primary constructor like shown below.
class ListTitle(
var id: Int? = null,
var title: String? = null
)
class ListDes(
var address: Int? = null,
var des: String? = null
)
Don't overuse nullability (using Int? instead of Int for instance). Make properties only nullable if necessary. If you always pass in arguments for the specified properties there is not need for them to be nullable.
Maybe you should choose other names for the classes (without "List" in it) since they are actually elements of a List in your example and not lists themselves.
If you just want to print the values you could do this:
listOfTitle.forEach {
val id = it.id
println(it.title + " " + listDes.filter { it.address == id }[0].des)
}
will print the matching des for each id:
Hello World Des
World Hello Des
The above code is supposed to work when both lists have the same length and there is always a matching des for each id
if you want to create a new list with the matching pairs:
val newList = listOfTitle.map { it ->
val id = it.id
Pair(it.title, listDes.filter { it.address == id }[0].des)
}
newList.forEach { println(it.first + " " + it.second) }

Assigning values to ArrayList using mapTo

Previously I was using this code:
private val mItems = ArrayList<Int>()
(1..item_count).mapTo(mItems) { it }
/*
mItems will be: "1, 2, 3, 4, 5, ..., item_count"
*/
Now, I am using a class instead of Int, but the class has Int member with name id.
class ModelClass(var id: Int = 0, var status: String = "smth")
So how can I use this method to fill the ArrayList in similar way?
//?
private val mItems = ArrayList<ModelClass>()
(1..item_count).mapTo(mItems) { mItems[position].id = it } // Something like this
//?
From the mapTo documentation:
Applies the given transform function to each element of the original collection and appends the results to the given destination.
Therefore, you just need to return the elements you want:
(1..item_count).mapTo(mItems) { ModelClass(it) }
If you are OK with any MutableList (which is often ArrayList or similar):
val mItems1 = MutableList(item_count) { i -> i }
val mItems2 = MutableList(item_count) { ModelClass(it) }