TimY - 1 year ago 84
Scala Question

Built-in method for rolling Scala List or Seq

I'm trying to roll a list, for example like so:

``````val notRolled = (1 to 5).toList       // List(1,2,3,4,5)

val rolledBy1 = rollList(notRolled,1) // List(2,3,4,5,1)
val rolledBy2 = rollList(notRolled,2) // List(3,4,5,1,2)
val rolledBy3 = rollList(notRolled,3) // List(4,5,1,2,3)
val rolledBy4 = rollList(notRolled,4) // List(5,1,2,3,4)
val rolledBy5 = rollList(notRolled,5) // List(1,2,3,4,5)

val rolledByM1 = rollList(notRolled,-1) // List(5,1,2,3,4)
val rolledByM2 = rollList(notRolled,-2) // List(4,5,1,2,3)
val rolledByM3 = rollList(notRolled,-3) // List(3,4,5,1,2)
val rolledByM4 = rollList(notRolled,-4) // List(2,3,4,5,1)
val rolledByM5 = rollList(notRolled,-5) // List(1,2,3,4,5)
``````

I had a look at scala-lang.org and can't seem to find anything that matches my requirement.

Is there a built-in method for rolling a list?

If not, is there a more efficient way to do it than this:

``````def rollList[T](theList: List[T], itemsToRoll: Int): List[T] = {
val split = {
if (itemsToRoll < 0) (theList.size + itemsToRoll % theList.size)
else itemsToRoll % theList.size
}
val (beginning, end) = theList.splitAt(split)
end ::: beginning
}
``````

Using the enrich-my-library pattern will allow you to add the `roll` method directly to all the collections so that you can call `myList.roll(2)`, as opposed to `roll(myList, 1)`.

Using the generic `CanBuildFrom` pattern allows you to make `roll` return the best possible type on these collections (so that `roll` on a `List` will return a `List`, but `roll` on an `Iterable` will return an `Iterable`).

All together, here is one option that abides by these patterns:

``````import scala.collection.generic.CanBuildFrom
import scala.collection.TraversableLike

implicit class TraversableWithRoll[A, Repr <: Traversable[A]](val xs: TraversableLike[A, Repr]) extends AnyVal {
def roll[That](by: Int)(implicit bf: CanBuildFrom[Repr, A, That]): That = {
val builder = bf()
val size = xs.size
builder.sizeHint(xs)
val leftBy = if (size == 0) 0 else ((by % size) + size) % size
builder ++= xs.drop(leftBy)
builder ++= xs.take(leftBy)
builder.result
}
}
``````

This allows you to do some of the following:

``````List(1, 2, 3, 4, 5).roll(2) //List(4,5,1,2,3)
Seq(3, 4, 5).roll(-1) //Seq(5, 3, 4)
``````

Notice the benefits of the fluent syntax enabled by the enrich-my-library pattern, and the stronger types enabled by the use of the `CanBuildFrom` implicit .

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download