Kotlinx.coroutines: Provide `associate` terminal operators

Created on 17 Sep 2019  路  11Comments  路  Source: Kotlin/kotlinx.coroutines

Hi,

Today we realized that despite the terminal operators toList and toSet being provided for Flow, there isn't anything to create a map from a Flow.

Could we have associate, associateBy and associateWith for Flow, as we have for Sequence?

flow use-case needed

Most helpful comment

Please consider Flow<Pair<K, V>>.toMap(), too.

All 11 comments

Definitely, we can implement these operators.

Initially, we've tried to avoid having the same variety of Flow operators as Sequence has because Flow has slightly different use-cases though it looks like a sequence. But if people keep lacking these operators for Flow, we will add them.

Please consider Flow<Pair<K, V>>.toMap(), too.

Yep, right now we have to workaround by doing .toList().toMap() - would be nice to be able to convert to a map out of the box.

I'have came here because i find the toList().toMap() workaround kinda ugly.
Should we create a separate issue?

Please, describe your specific use-cases in this issue.

i have functions that returns a Flow<Pair<K, V>>.
To convert this into a Map i have to use toList().toMap().
Having a terminal operator toMap on flow would elimante an intermediate collection created by toList and thus increase the performance.

@Xaseron,
you should to explain a bit deeper your use case.

However you can consider to include an extension function like:

suspend fun <K, V> Flow<Pair<K, V>>.toMap(): Map<K, V> {
    val map = mutableMapOf<K, V>()
    collect { (k, v) ->
        map[k] = v
    }
    return map
}

Having this in the stdlib would make it even with collection api.
Isn't this a goal?

@Xaseron So, what's your use-case? Can you elaborate, please.

I have this method where i use a Mongodb Aggregation Pipeline to gather all values for specific year/month in a list. Afterwards i'm calling collect on this publisher to map each year/month to the average value of this list and emit it.

    fun overallTrends(metricName: String) = flow {
        val mongoList = col.aggregate<OverallTrend>(
                """
[
    { $ group:
        {
             _id: {
                year: "$ metaData.reportingYear",
                month: "$ metaData.reportingMonth"
            },
            values: { $ push: "$ $metricName" }
        }
    }
]""".formatJson()
        )
        mongoList.collect { trend ->
            val yearMonth = trend.id.toYearMonth()
            emit(yearMonth to trend.averageValue)
        }
    }

I want to call a terminal operation on this flow to get a Map.
Therefore currently i have to it this way:
val overallTrendsMap = mongoMetricsService.overallTrends(mongoMetricName).toList().toMap()
I would be more performant and nicer to read if i could directly call it like this:
val overallTrendsMap = mongoMetricsService.overallTrends(mongoMetricName).toMap()

Why do you define your overallTrends function to return a flow if you plan to get get a map out of it? Wouldn't it be easier to define it as a suspending function that returns a map in the first place?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

petersamokhin picture petersamokhin  路  3Comments

jaozinfs picture jaozinfs  路  3Comments

mhernand40 picture mhernand40  路  3Comments

ZakTaccardi picture ZakTaccardi  路  3Comments

iTanChi picture iTanChi  路  3Comments