Skip to content

Flow: Intermediate operators

Devrath edited this page Jan 19, 2024 · 16 revisions
  • These intermediate operators are also called transform operators
  • These operators are executed for every emission of the flow
  • It returns a new and transformed flow

Map

Screenshot 2023-12-27 at 6 24 43β€―PM

Observation

  • Using the map-operator we can convert the form of one type of input into another type
  • In the example below, We have converted from integer to String

It seems like you want to create a table in a GitHub README file to describe three operators for Kotlin's Flow API: map, mapNotNull, and mapLatest. Below is an example table format that you can use in your README file:

Operator Description
map Transforms each emitted item by applying a given function, producing a new flow of transformed items.
mapNotNull Transforms each emitted item by applying a given function, excluding null results from the new flow.
mapLatest Similar to map, but only emits the result of the latest transformation when a new item is emitted.

You can copy and paste this table into your README file and fill in the descriptions for each operator. Additionally, you can provide examples or usage patterns for better understanding. Here's a more detailed explanation of each operator:

  1. map: The map operator transforms each item emitted by the flow by applying a given function. It creates a new flow that emits the transformed items. This is useful when you want to perform a one-to-one transformation on each item in the flow.

  2. mapNotNull: Similar to map, the mapNotNull operator transforms each item emitted by the flow using a given function. However, it excludes null results from the new flow. This is useful when you want to filter out null values resulting from the transformation.

  3. mapLatest: The mapLatest operator is similar to map, but it only emits the result of the latest transformation when a new item is emitted in the original flow. If a new item is emitted before the previous transformation completes, the result of the previous transformation is ignored. This is useful when you want to ensure that only the latest result is considered, discarding outdated transformations.

Code

private val flowOfIntegers = flowOf(1, 2, 3, 4, 5)

viewModelScope.launch {
            flowOfIntegers.map {
                it.toString()
            }.collect {
                if(it is String){
                    // Use smart cast to check if it is string
                    println("Received value: -->$it")
                }
            }
}

Output

Received value: -->1
Received value: -->2
Received value: -->3
Received value: -->4
Received value: -->5

Filter

Screenshot 2023-12-27 at 6 27 29β€―PM

Observation

  • Here we pass a collection of items but emit only those that pass a predicate test
Operator Description
flowOfIntegers.filter { } Filters items emitted by the flow based on a given predicate. Only items that satisfy the predicate are included in the new flow.
flowOfIntegers.filterNot { } Filters items emitted by the flow based on a given predicate. Only items that do not satisfy the predicate are included in the new flow.
flowOfIntegers.filterIsInstance<Int>{ } Filters items emitted by the flow and includes only those that are instances of the specified type (Int in this case).

Code

        val flowOfIntegers = flowOf(1, 2, 3, 4, 5)

        viewModelScope.launch {
            flowOfIntegers.filter {
                // We use modulus function to check if the number is divisible by 2
                it%2 == 0
            }.collect{
                println("Received value: -->$it")
            }
        }

Output

Received value: -->2
Received value: -->4

Take

Screenshot 2023-12-27 at 7 44 48β€―PM

Observation

  • It is called a sizing operator
Operator Description
flowOfIntegers.take { } Limits the number of items emitted by the flow to a specified count.
flowOfIntegers.takeIf { } Conditional operator; takes items from the flow if a specified condition is true, otherwise returns an empty flow.
flowOfIntegers.takeWhile { } Takes items from the flow as long as a specified predicate is true; stops when the predicate becomes false.
flowOfIntegers.takeUnless { } Opposite of takeIf; takes items from the flow if a specified condition is false, otherwise returns an empty flow.

Code

        val scope = CoroutineScope(EmptyCoroutineContext)
        scope.launch {
            flowOfIntegers.take(2).collect{
                println("Received value: -->$it")
            }
        }

Output

Received value: -->1
Received value: -->2

Difference between Filter and Take

  • Filter does not cancel the upstream flow when the conditions are not met, Actually it just forwards the filtered flow.
  • Take cancel the upstream flow when a certain condition is not met.

Drop

Observation

  • Here we can drop an initial specified number of emissions in the flow and allow the rest
Operator Description
flowOfIntegers.drop { } Drops a specified number of items from the beginning of the flow.
flowOfIntegers.dropWhile { } Drops items from the beginning of the flow as long as a specified predicate is true; emits the rest of the items.

Code

        val scope = CoroutineScope(EmptyCoroutineContext)
        scope.launch {
            flowOfIntegers.drop(1).collect{
                println("Received value: -->$it")
            }
        }

Output

Received value: -->2
Received value: -->3
Received value: -->4
Received value: -->5

Transform

Observation

  • We can transform the data apply some predicate conditions and emit the new value,
  • We can also emit multiple emissions
  • This is basically applying a collection of operators together and emitting the emission downstream

Output

Received value: -->2
Received value: -->4

Code

fun transformOperator() {
        val scope = CoroutineScope(EmptyCoroutineContext)
        scope.launch {
            flowOfIntegers.transform {
                if((it%2)==0){
                    // Elements that are divisible by 2
                    emit(it)
                }
            }.collect{
                println("Received value: -->$it")
            }
        }
}

Debounce

Observation

  • Using this operator we can discard the emissions in a certain period frame.
  • Some emissions will be lost from this time frame
  • This is helpful to drop some duplicate emissions from the period frame.
  • One example is when the mouse is moving and a lot of emissions might be sent from the position coordinates of the mouse, and we might be interested in certain coordinates in it.
Clone this wiki locally