Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin - Check collection contains another collection exactly

Tags:

kotlin

I want to be able to check that collection a contains exactly all of the elements of b, but an equality based check is not sufficient; for example:

data class Person(val name: String, val age: Int)

val a = listOf(Person("Alice", 10), Person("Bob", 13))
val b = listOf(Person("Alice", 10), Person("Alice", 10))

fun main() {
   println(a.containsAll(b))
}

true

Whilst this is technically true, it's not the result I want, because a only contains one Person("Alice", 10), whereas b contains two of them.

The above example should fail, whilst the below should pass.

data class Person(val name: String, val age: Int)

val a = listOf(Person("Alice", 10), Person("Alice", 10), Person("Bob", 13))
val b = listOf(Person("Alice", 10), Person("Alice", 10))

fun main() {
   println(a.containsAllExact(b))
}

Is there a way to do this?

like image 876
Matthew Layton Avatar asked Nov 22 '21 13:11

Matthew Layton


3 Answers

You could add an extension function for this, something like:

fun List<Person>.containsAllExact(list2: List<Person>): Boolean {
    val occurences1 = this.groupingBy{ it }.eachCount()
    val occurences2 = list2.groupingBy{ it }.eachCount()
    return occurences2.all{
        it.value <= occurences1.getOrDefault(it.key, 0)
    }

}
like image 147
somethingsomething Avatar answered Oct 20 '22 19:10

somethingsomething


One way I can think of is to make a mutable copy of a, and try to remove every element of b, then check if all elements of b can be removed:

println(
    a.toMutableList().let { aCopy ->
        b.all(aCopy::remove)
    }
)

Or as an extension function:

fun <T> Iterable<T>.strictlyContainsAll(other: Iterable<T>) = 
    toMutableList().let { copy -> 
        other.all(copy::remove)
    }
like image 3
Sweeper Avatar answered Oct 20 '22 19:10

Sweeper


I think this would do it

println(a.containsAll(b) && (a.size - b.size == (a-b).size))

Edit: it doesn't work because it gives false negatives also. for example with

val a = listOf(Person("Alice", 10), Person("Alice", 10), Person("Bob", 13))
val b = listOf(Person("Alice", 10))
like image 1
Ivo Beckers Avatar answered Oct 20 '22 19:10

Ivo Beckers