Search code examples
androidkotlinkotlin-coroutineskotlin-flow

Collect elements of the flow in a different order


I have a flow of the events, there are two types of events - initial or non-initial.

data class Event(
    isInitial: boolean,
)

val flow: MutableStateFlow<Event?> = MutableStateFlow(null)

Initial event might be produced only once. Non-initial events might be produced many times before or after initial.

In my view model I need to collect the events, but the initial events should be processed first, then non-initial, even if they were emitted earlier.

How to achieve this behaviour? What flow operators would help?


Solution

  • I don't know of any ready to use operators like this, but we can easily write our own, by temporarily storing items in the buffer:

    fun <T> Flow<T>.pullSingleToFront(predicate: (T) -> Boolean) = flow {
        val buf = mutableListOf<T>()
        var found = false
        collect { item -> when {
            found -> emit(item)
            !predicate(item) -> buf += item
            else -> {
                found = true
                emit(item)
                buf.forEach { emit(it) }
                buf.clear()
            }
        }}
    }
    

    Example:

    suspend fun main() {
        flow {
            emit(Event(2))
            emit(Event(3))
            emit(Event(1, isInitial = true))
            emit(Event(4))
            emit(Event(5))
        }
            .pullSingleToFront { it.isInitial }
            .collect { println(it) } // 1, 2, 3, 4, 5
    }
    
    data class Event(
        val id: Int,
        val isInitial: Boolean = false,
    )
    

    Still, this kind of requirements doesn't fit flows very well. I agree with @tyg that maybe you need a code redesign of some sort.