16
votes

If I want to collect my Kotlin collection into something that isn't built into the stdlib, how do I do it?

2

2 Answers

21
votes

For scenarios not covered by built in operations toList() etc, you can use the fact that collect is just a fold. So given

val list: List<Pair<String, Int>> = listOf("Ann" to 19, "John" to  23)

you can collect to a collection of your choice with a fold

val map: Map<String, Int> = list.fold(HashMap(), { accumulator, item ->
    accumulator.put(item.first, item.second); accumulator})

If you then define an extension function

fun <T, R> Iterable<T>.collectTo(accumulator: R, accumulation: (R, T) -> Unit) = 
    this.fold(accumulator, { accumulator, item -> accumulation(accumulator, item); accumulator } )

you can further simplify

val map2: Map<String, Int> = list.collectTo(HashMap(), { accumulator, item -> 
    accumulator.put(item.first, item.second) })

Although in this case of course you could just use the .toMap extension function.

8
votes

NOTE: For some common cases where you would use Stream.collect from Java 8, you can use already existing stdlib functions in Kotlin. For those you cannot find in Kotlin you can explore creating custom flows as described in the answer by @duncan. This answer is added to help know what is capable within stdlib, so you can decide when to write something custom. It answers the title of the question "What is the Kotlin equivalent of Java Stream.collect?"

There are functions in the stdlib for average, count, distinct,filtering, finding, grouping, joining, mapping, min, max, partitioning, slicing, sorting, summing, to/from arrays, to/from lists, to/from maps, union, co-iteration, all the functional paradigms, and more. Little 1-liners and no need to use the more complicated syntax of Java 8. I think the only thing missing from the built-in Java 8 Collectors class is summarization.

Another SO Post covers these equivalencies in detail is: What Java 8 Stream.collect equivalents are available in the standard Kotlin library?