I’m not a regular StackOverflow user, neither as a contributor, nor to ask questions. However, I recently stumbled upon a question, tried to help… and failed. I was pointed out my answer was wrong - thanks for everyone who did. I think failures are great occasions to learn something new!
In this post, I’d like to detail what I learned after digging further: there are several ways to combine elements from an existing collection in Kotlin stdlib.
Combining with next
The fist combining function is to associate one element with the next, in pairs.
The signature is:
fun <T> Iterable<T>.zipWithNext(): List<Pair<T, T>>
Here’s a simple example on how to use it:
val elements = listOf("A", "B", "C", "D", "E", "F", "G", "H")
elements.zipWithNext().forEach {
print(it)
}
It will output:
(A, B)(B, C)(C, D)(D, E)(E, F)(F, G)(G, H)
Another flavor of zipWithNext
is:
fun <T, R> Iterable<T>.zipWithNext(
transform: (a: T, b: T) -> R
): List<R>
This allows to run a transformation function on the pairs, e.g.:
elements.zipWithNext { first, second ->
"$first$second "
}.forEach {
print(it)
}
The corresponding output is:
AB BC CD DE EF FG GE
Combining using chunks
The second combining function is to combine elements in chunks of a specific size:
fun <T> Iterable<T>.chunked(size: Int): List<List<T>>
Here’s how to use it:
elements.chunked(2).forEach {
print(it)
}
It yields the following:
[A, B][C, D][E, F][G, H]
As for the previous function, there’s an alternative flavor using a transform function:
fun <T, R> Iterable<T>.chunked(
size: Int,
transform: (List<T>) -> R
): List<R>
Using it is quite straightforward:
elements.chunked(3) {
print("${it.joinToString("")} ")
}
The result is:
ABC DEF GH
The size of the last chunk may not be the same as the regular chunk size. That is the case if the remainder of the division of the collection’s size by the chunk’s size is not 0. |
Combining using sliding window
The final combining function uses a sliding window over the elements in the collection. Parameters include the window "width" and the sliding "step":
fun <T> Iterable<T>.windowed(
size: Int,
step: Int = 1,
partialWindows: Boolean = false
): List<List<T>>
It’s a generalization of the first two functions:
zipWithNext == windowed(2, 1)
There’s a slight difference, as
zipWithNext()
returnsPair
, whilewindowed()
returnsList
The exact equivalence should be:
zipWithNext().map { listOf(it.first, it.second) } = windowed(2, 1)
chunked(x) == windowed(x, x, true)
As the 2 previous functions, windowed
also offers a transform flavor.