I want to infinitely repeat T
elements in a Sequence<T>
. This can't be done using kotlin.collections.asSequence. For example:
val intArray = intArrayOf(1, 2, 3)
val finiteIntSequence = intArray.asSequence()
val many = 10
finiteIntSequence.take(many).forEach(::print)
// 123
This is not what I want. I expected some kind of kotlin.collections.repeat
function to exist, but there isn't, so I implemented one myself (e.g. for this IntArray
):
var i = 0
val infiniteIntSequence = generateSequence { intArray[i++ % intArray.size] }
infiniteIntSequence.take(many).forEach(::print)
// 1231231231
This is quite imperative, so I feel there must be a more functional and less verbose way to do this. If it exists, what is/are Kotlin's standard way(s) to repeat collections / arrays a(n) (in)finite amount of times?
Example 1 – Kotlin repeat() In this example, we will use a repeat statement to execute print statement 4 times. fun main(args: Array) { repeat(4) { println("Hello World!") } }
From a function One more way to create a sequence is by building it with a function that calculates its elements. To build a sequence based on a function, call generateSequence() with this function as an argument. Optionally, you can specify the first element as an explicit value or a result of a function call.
The key difference lies in the semantics and the implementation of the stdlib extension functions for Iterable<T> and Sequence<T> . For Sequence<T> , the extension functions perform lazily where possible, similarly to Java Streams intermediate operations. For example, Sequence<T>.
Update: coroutines are no longer experimental as of Kotlin 1.3! Use them as much as you like :)
If you allow the use of coroutines you can do this in a pretty clean way using sequence
:
an infinite amount of times
fun <T> Sequence<T>.repeat() = sequence { while (true) yieldAll(this@repeat) }
Note the use of a qualified this
expression this@repeat
- simply using this
would refer to the lambda's receiver, a SequenceScope
.
then you can do
val intArray = intArrayOf(1, 2, 3)
val finiteIntSequence = intArray.asSequence()
val infiniteIntSequence = finiteIntSequence.repeat()
println(infiniteIntSequence.take(10).toList())
// ^ [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
a finite amount of times
fun <T> Sequence<T>.repeat(n: Int) = sequence { repeat(n) { yieldAll(this@repeat) } }
To avoid using the experimental coroutines, use:
generateSequence { setOf("foo", 'b', 'a', 'r') }
.flatten() // Put the Iterables' contents into one Sequence
.take(5) // Take 5 elements
.joinToString(", ")
// Result: "foo, b, a, r, foo"
or alternatively, if you want to repeat the entire collection a number of times, just take before flattening:
generateSequence { setOf("foo", 'b', 'a', 'r') }
.take(5) // Take the entire Iterable 5 times
.flatten() // Put the Iterables' contents into one Sequence
.joinToString(", ")
// Result: "foo, b, a, r, foo, b, a, r, foo, b, a, r, foo, b, a, r, foo, b, a, r"
For the original question's IntArray
, the array first must be converted to an Iterable<Int>
(otherwise flatten()
isn't available):
val intArray = intArrayOf(1, 2, 3)
generateSequence { intArray.asIterable() }
.flatten()
.take(10)
.joinToString(", ")
// Result: "1, 2, 3, 1, 2, 3, 1, 2, 3, 1"
Furthermore, other types of Array
, e.g. ByteArray
or LongArray
, as well as Map
are not Iterable
, but they all implement the asIterable()
method like IntArray
in the example above.
I think this is pretty clear:
generateSequence(0) { (it + 1) % intArray.size }
.map { intArray[it] }
.forEach { println(it) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With