Scamparelli
Scamparelli

Reputation: 744

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.

Upvotes: 1

Views: 616

Answers (3)

Kyouraku88
Kyouraku88

Reputation: 156

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{...}

Upvotes: 0

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

Upvotes: 0

Hamza Israr
Hamza Israr

Reputation: 441

Use equals property of Data Class:

val matchingSites: List<SiteObject> = allSites
    .filterNotNull()
    .filter { it.equals(currentSite) }

Upvotes: 1

Related Questions