how to find an index in Arraylist of custom object based on its specific properties in Kotlin? - 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))

Related

Regex and .contains

I am recognizing words from an Imageview using FirbaseVisionImage class. I want to determined a substring which has a regex pattern of ( "^\\d{6}-\\d{2}-\\d{4}\$") from the whole string from an Imageview, and then show the substring which has the regex pattern of ( "^\\d{6}-\\d{2}-\\d{4}\$") at a textview, however it won't work
val rotatedBitmap = rotateBitmap(bitmap, 270f)
val firebaseVisionImage = FirebaseVisionImage.fromBitmap(rotatedBitmap)
val firebaseVision = FirebaseVision.getInstance()
val firebaseVisionTextRecognizer = firebaseVision.onDeviceTextRecognizer
val task: Task<FirebaseVisionText> =
firebaseVisionTextRecognizer.processImage(firebaseVisionImage)
task.addOnSuccessListener { p0 ->
var s = p0?.text
val regex = Regex( "^\\d{6}-\\d{2}-\\d{4}\$")
var icFormat = ""
var subString = regex.matches(icFormat)
if(s!!.contains(subString.toString())){
binding.tvDemo.text = subString.toString()
}else{
binding.tvDemo.text = "Cannot Detect Regex"
}
}

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 get the index of a gson object?

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" }

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) }

How to get #Query( nativeQuery=true) result into List<MyObject>?

Hello I have a query and I want the result into list of objects, not entity. But the result is actualy a object which I should transfer to my object. Is there a way to map it directly to my custom object?
Maybe this will help you :
public interface ObjRepository extends JpaRepository<MyObject, Long> {
#Query(value = "FROM MyObject WHERE objname = ?1")
public List<MyObject> findByName(String name);
}
Approach-1: using List of object array.
When using native query, we get list of Object array i.e each row in list is array. Elements in array represent column values.
In repo interface:
#Query(value = "select col1, col2, col3 from table1 where col1 = :key", nativeQuery = true)
List<Object[]> findByKey(#Param("key") String key);
In caller
List<Object[]> objectList = new ArrayList<Object[]>();
objectList = repo.findByKey(key);
List<CustomObject> customObjectList = new ArrayList<>();
for (Object[] tuple : objectList) {
String col1 = (String) tuple[0];
String col2 = (String) tuple[1];
String col3 = (String) tuple[2];
CustomObject obj = new CustomObject();
obj.setCol1(col1);
obj.setCol2(col2);
obj.setCol3(col3);
customObjectList.add(obj);
}
return customObjectList;
Approach-2: using custom dto that represents columns in each row.
Refer https://stackoverflow.com/a/42905382/1358551
final List<MyCustomDTO> statuses = myRepository
.findStatuses(marketId, campaignId, stationIds).stream()
.map(o -> new MyCustomDTO(((BigInteger) o[0]), (Boolean) o[1], (Timestamp) o[2]))
.collect(toList());
public class StationStatusDTO {
private long id;
private boolean isSomething;
private LocalDateTime date;
public MyCustomDTO(BigInteger id, Boolean isSomething, Timestamp date) {
this(id.longValue(),
isSomething,
(date == null) ? null : LocalDateTime
.ofInstant(Instant.ofEpochMilli(date.getTime()),
TimeZone.getDefault().toZoneId()));
}