Erik
Erik

Reputation: 4573

How do I infinitely repeat a sequence in Kotlin?

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

Answers (6)

Daniel K.
Daniel K.

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

pablisco
pablisco

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

Salem
Salem

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

Erik
Erik

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

s1m0nw1
s1m0nw1

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

s1m0nw1
s1m0nw1

Reputation: 82117

I think this is pretty clear:

generateSequence(0) { (it + 1) % intArray.size }
        .map { intArray[it] }
        .forEach { println(it) }

Upvotes: 3

Related Questions