Description
Use case
I have a complex Flow
that possesses many elements. I have a business rule that is, literally, to do something if any of them satisfies a condition.
If I were using List
or Set
, I would use any { theCondition(it) }
. However, Flow
doesn't seem to have any
.
There is an old issue (#2239) that asks for this feature, and is closed because of alternative implementations:
suspend fun <T : Any> Flow<T>.any(predicate: suspend (T) -> Boolean): Boolean { return this.firstOrNull { predicate(it) } != null } suspend fun <T> Flow<T>.all(predicate: suspend (T) -> Boolean): Boolean { return this.count { !predicate(it) } == 0 }
I dislike this solution because:
- The first one doesn't work with a
Flow<Foo?>
becausefirstOrNull
is only available on non-nullable types. - Both options obfuscate the operation I am actually attempting to perform.
I believe it is worth having any
/all
/none
directly in the library because the proposed implementation have downsides.
The Shape of the API
suspend fun <T> Flow<T>.any(predicate: suspend (T) -> Boolean): Boolean
suspend fun <T> Flow<T>.all(predicate: suspend (T) -> Boolean): Boolean
suspend fun <T> Flow<T>.none(predicate: suspend (T) -> Boolean): Boolean
Prior Art
These three functions are already available on Iterable
and Sequence
. The behavior should be the same.
all
and none
can use the count
operator under the hood, since it already shortcuts. I believe this is a possible implementation of any
, though I haven't tested it yet:
suspend fun <T> Flow<T>.any(predicate: suspend (T) -> Boolean) = this
.transformWhile {
if (predicate(it)) {
emit(true)
false // ONE element MATCHES, no need to go further
} else {
true // continue
}
}
.onEmpty { emit(false) }
.first()
I can submit a PR if you think this is worth pursuing.