Kotlin Collection extension function .all {} produce true for empty collection. Solution !
What is the problem with Kotlin .all {} extension function from Kotlin Collection?
Documentation :
/**
* Returns `true` if all elements match the given [predicate].
*
* @sample samples.collections.Collections.Aggregates.all
*/
public inline fun <T> Iterable<T>.all(predicate: (T) -> Boolean): Boolean {
if (this is Collection && isEmpty()) return true
for (element in this) if (!predicate(element)) return false
return true
}
So if you have a collection … let’s say like this :
val list = listOf<String>("OK", "OK")
and you want to check if all elements match the given condition: “are all elements in collection equals “OK”?
You can try this :
val isTrue : Boolean = list.all { it == "OK" }
And you will receive a “TRUE”.
Everything looks GOOD … so what is the problem?
Let's say we receive our “list” from backend and it is possible to receive an empty list (not null but empty).
Try to guess what will be the answer in the given case?
val list = listOf<String>()
val isTrue : Boolean = list.all { it == "OK" }
I thought it will produce a “FALSE” value. But … unfortunately, it produces a “TRUE”.
In my opinion, it is confusing.
Why when the list is empty we receive the answer that all elements inside are “OK”? There are no elements so it is not true ?! 😏
To handle this case we can create this type of solution :
val isTrue = if (list.isEmpty()) {
false
} else {
list.all { it == "OK" }
}
If the collection is empty return false and if it is not empty then check the predicate. It works but looks ugly and we need to remember to handle this case in every place.
My solution for this problem is to create another extension function which will produce false in case of the empty list.
So I named it “.only” and its implementation looks like this :
public inline fun <T> Iterable<T>.only(predicate: (T) -> Boolean): Boolean {
if (this is Collection && isEmpty()) return false
for (element in this) if (!predicate(element)) return false
return true
}
With this solution, we will receive a “FALSE” for the empty list.
val list = listOf<String>()
val isTrue : Boolean = list.only { it == "OK" }
For notEmpty() list it works the same as .all {}
Here are some test cases for Kotlin .all {} and my own .only {} extension function implementation which passes and proves all works great.
Try it yourself 😄
class GeneralExtensionsKtTest {
@Test
fun `only extension function should return false if not all elements match a definition`() {
val list = listOf("OK", "Not OK")
assertThat(list.only { it == "OK" }).isFalse()
}
@Test
fun `only extension function should return true if all elements match a definition`() {
val list = listOf("OK", "OK")
assertThat(list.only { it == "OK" }).isTrue()
}
@Test
fun `only extension function should return false if list is not empty but elements not match a definition`() {
val list = listOf("Not OK")
assertThat(list.only { it == "OK" }).isFalse()
}
@Test
fun `only extension function should return false if list is empty`() {
val list = listOf<String>()
assertThat(list.only { it == "OK" }).isFalse()
}
@Test
fun `standard Kotlin all extension function for empty list will return true`() {
val list = listOf<String>()
assertThat(list.all { it == "OK" }).isTrue()
}
}