Kotlin flow: collect only elements which repeat at least N times

Viewed 5295

I am implementing ML Kit OCR feature and sometimes, the first several values are wrong and only after some time the camera stabilizes and produces correct values. I do not want to drop first X values because I do not know how many elements the flow would contain. So the best way would be to use some condition, which compares the current element with the previous one, but not sure.

Is there a function in Kotlin Flow API which will compare collected values and will collect only the one which occurred at least N times?

  private val _detectedValues = ConflatedBroadcastChannel<String>()
  val detectedFlow = _detectedValues
      .asFlow()
      .map { it.replace(" ", "") }
      .filter { it.checkRegex() }
      .onEach {
          Log.i(TAG, "detected: $it")
      }
1 Answers

There's no built-in way to do it as far as I know, but you can make one:

fun <T> Flow<T>.repeated(times: Int): Flow<T> {
    val map = mutableMapOf<T, Int>()
    return transform { value ->
        val count = (map[value] ?: 0) + 1
        map[value] = count
        if (count >= times) {
            return@transform emit(value)
        }
    }
}

This uses a map to keep track of how many times each value has been emitted. Then, if a value is emitted enough times, it gets emitted through the transformation.

channel.asFlow()
        .repeated(3)
        .collect {
            // Do something.
        }

EDIT: It most definitely works. Here's the test code:

suspend fun test() = coroutineScope {
    val flow = MutableSharedFlow<String>()
    launch {
        flow.repeated(3).collect { value ->
            println("received: $value")
        }
    }
    launch {
        while (true) {
            val value = listOf("a", "b", "c").random()
            println("emitted: $value")
            flow.emit(value)
            delay(1000)
        }
    }
}

I get the following output:

emitted: c
emitted: a
emitted: c
emitted: c
received: c
emitted: b
emitted: b
emitted: b
received: b
emitted: a
emitted: a
received: a
emitted: a
received: a
emitted: b
received: b
Related