1

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.

3 Answers 3

1

Use equals property of Data Class:

val matchingSites: List<SiteObject> = allSites
    .filterNotNull()
    .filter { it.equals(currentSite) }
Sign up to request clarification or add additional context in comments.

2 Comments

Ah, thanks Hamza - but does this compare objects where ANY of the fields match or only does it only match if ALL the fields match? If the latter, it's not what I'm looking for..?
Yup. It's for ALL. Then the ideal solution is to create a custom compare function in SiteObject and use it in filter instead of 'equals'.
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
        }
    }
}

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.