If I want to collect my Kotlin collection into something that isn't built into the stdlib, how do I do it?
2 Answers
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.
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?