Reputation: 4573
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?
Upvotes: 19
Views: 12920
Reputation: 5917
If you happen to have Guava on your classpath, you can do the following:
val intArray = intArrayOf(1, 2, 3)
val cyclingSequence = Iterators.cycle(intArray.asList()).asSequence()
// prints 1,2,3,1,2,3,1,2,3,1
println(cyclingSequence.take(10).joinToString(","))
Upvotes: 0
Reputation: 14237
I'm unsure if this is due to API changes in Kotlin, but it's possible to do the following:
fun <T> Sequence<T>.repeatForever() =
generateSequence(this) { it }.flatten()
Live example: https://pl.kotl.in/W-h1dnCFx
Upvotes: 3
Reputation: 14967
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) } }
Upvotes: 22
Reputation: 4573
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.
Upvotes: 5
Reputation: 82117
A generic solution would be to reuse the proposal from this answer with extension functions:
fun <T> Array<T>.asRepeatedSequence() =
generateSequence(0) {
(it + 1) % this.size
}.map(::get)
fun <T> List<T>.asRepeatedSequence() =
generateSequence(0) {
(it + 1) % this.size
}.map(::get)
Called like this:
intArray.asRepeatedSequence().forEach(::println)
Upvotes: 2
Reputation: 82117
I think this is pretty clear:
generateSequence(0) { (it + 1) % intArray.size }
.map { intArray[it] }
.forEach { println(it) }
Upvotes: 3