All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.flow.operators.Distinct.kt Maven / Gradle / Ivy

@file:JvmMultifileClass
@file:JvmName("FlowKt")

package kotlinx.coroutines.flow

import kotlinx.coroutines.flow.internal.*
import kotlin.jvm.*

/**
 * Returns flow where all subsequent repetitions of the same value are filtered out.
 *
 * Note that any instance of [StateFlow] already behaves as if `distinctUntilChanged` operator is
 * applied to it, so applying `distinctUntilChanged` to a `StateFlow` has no effect.
 * See [StateFlow] documentation on Operator Fusion.
 * Also, repeated application of `distinctUntilChanged` operator on any flow has no effect.
 */
public fun  Flow.distinctUntilChanged(): Flow =
    when (this) {
        is StateFlow<*> -> this // state flows are always distinct
        else -> distinctUntilChangedBy(keySelector = defaultKeySelector, areEquivalent = defaultAreEquivalent)
    }

/**
 * Returns flow where all subsequent repetitions of the same value are filtered out, when compared
 * with each other via the provided [areEquivalent] function.
 *
 * Note that repeated application of `distinctUntilChanged` operator with the same parameter has no effect.
 */
@Suppress("UNCHECKED_CAST")
public fun  Flow.distinctUntilChanged(areEquivalent: (old: T, new: T) -> Boolean): Flow =
    distinctUntilChangedBy(keySelector = defaultKeySelector, areEquivalent = areEquivalent as (Any?, Any?) -> Boolean)

/**
 * Returns flow where all subsequent repetitions of the same key are filtered out, where
 * key is extracted with [keySelector] function.
 *
 * Note that repeated application of `distinctUntilChanged` operator with the same parameter has no effect.
 */
public fun  Flow.distinctUntilChangedBy(keySelector: (T) -> K): Flow =
    distinctUntilChangedBy(keySelector = keySelector, areEquivalent = defaultAreEquivalent)

private val defaultKeySelector: (Any?) -> Any? = { it }

private val defaultAreEquivalent: (Any?, Any?) -> Boolean = { old, new -> old == new }

/**
 * Returns flow where all subsequent repetitions of the same key are filtered out, where
 * keys are extracted with [keySelector] function and compared with each other via the
 * provided [areEquivalent] function.
 *
 * NOTE: It is non-inline to share a single implementing class.
 */
private fun  Flow.distinctUntilChangedBy(
    keySelector: (T) -> Any?,
    areEquivalent: (old: Any?, new: Any?) -> Boolean
): Flow = when {
    this is DistinctFlowImpl<*> && this.keySelector === keySelector && this.areEquivalent === areEquivalent -> this // same
    else -> DistinctFlowImpl(this, keySelector, areEquivalent)
}

private class DistinctFlowImpl(
    private val upstream: Flow,
    @JvmField val keySelector: (T) -> Any?,
    @JvmField val areEquivalent: (old: Any?, new: Any?) -> Boolean
): Flow {
    override suspend fun collect(collector: FlowCollector) {
        var previousKey: Any? = NULL
        upstream.collect { value ->
            val key = keySelector(value)
            @Suppress("UNCHECKED_CAST")
            if (previousKey === NULL || !areEquivalent(previousKey, key)) {
                previousKey = key
                collector.emit(value)
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy