10

I can do this quite easily, and cleanly, using a for loop. For instance, if I wanted to traverse a Seq from every element back to itself I would do the following:

val seq = Seq(1,2,3,4,5)

for (i <- seq.indices) {
    for (j <- seq.indices) {
        print(seq(i + j % seq.length))
    }
}

But as I'm looking to fold over the collection, I'm wondering if there is a more idiomatic approach. A recursive approach would allow me to avoid any vars. But basically, I'm wondering if something like the following is possible:

seq.rotatedView(i)

Which would create a rotated view, like rotating bits (or circular shift).

Nadir Muzaffar
  • 4,772
  • 2
  • 32
  • 48

11 Answers11

21

Is it like below:

scala> def rotatedView(i:Int)=Seq(1,2,3,4,5).drop(i)++Seq(1,2,3,4,5).take(i)
rotatedView: (i: Int)Seq[Int]

scala> rotatedView(1)
res48: Seq[Int] = List(2, 3, 4, 5, 1)

scala> rotatedView(2)
res49: Seq[Int] = List(3, 4, 5, 1, 2)
Binzi Cao
  • 1,075
  • 5
  • 14
  • 6
    also calling `.view` in front of `drop` and `take` will increase performance, as collection will not be copied. – Aivean Sep 22 '15 at 01:36
8

This ought to do it in a fairly generic way, and allow for arbitrary rotations:

def rotateLeft[A](seq: Seq[A], i: Int): Seq[A] = {
    val size = seq.size
    seq.drop(i % size) ++ seq.take(i % size)
}

def rotateRight[A](seq: Seq[A], i: Int): Seq[A] = {
    val size = seq.size
    seq.drop(size - (i % size)) ++ seq.take(size - (i % size))
}

The idea is simple enough, to rotate left, drop the first i elements from the left, and take them again from the left to concatenate them in the opposite order. If you don't mind calculating the size of the collection, you can do your operations modulo the size, to allow i to be arbitrary.

scala> rotateRight(seq, 1)
res34: Seq[Int] = List(5, 1, 2, 3, 4)

scala> rotateRight(seq, 7)
res35: Seq[Int] = List(4, 5, 1, 2, 3)

scala> rotateRight(seq, 70)
res36: Seq[Int] = List(1, 2, 3, 4, 5)

Similarly, you can use splitAt:

def rotateLeft[A](seq: Seq[A], i: Int): Seq[A] = {
    val size = seq.size
    val (first, last) = seq.splitAt(i % size)
    last ++ first
}

def rotateRight[A](seq: Seq[A], i: Int): Seq[A] = {
    val size = seq.size
    val (first, last) = seq.splitAt(size - (i % size))
    last ++ first
}

To make it even more generic, using the enrich my library pattern:

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

implicit class TraversableExt[A, Repr <: TraversableLike[A, Repr]](xs: TraversableLike[A, Repr]) {

    def rotateLeft(i: Int)(implicit cbf: CanBuildFrom[Repr, A, Repr]): Repr = {
        val size = xs.size
        val (first, last) = xs.splitAt(i % size)
        last ++ first
    }

    def rotateRight(i: Int)(implicit cbf: CanBuildFrom[Repr, A, Repr]): Repr = {
        val size = xs.size
        val (first, last) = xs.splitAt(size - (i % size))
        last ++ first
    }

}

scala> Seq(1, 2, 3, 4, 5).rotateRight(2)
res0: Seq[Int] = List(4, 5, 1, 2, 3)

scala> List(1, 2, 3, 4, 5).rotateLeft(2)
res1: List[Int] = List(3, 4, 5, 1, 2)

scala> Stream(1, 2, 3, 4, 5).rotateRight(1)
res2: scala.collection.immutable.Stream[Int] = Stream(5, ?)

Keep in mind these are not all necessarily the most tuned for performance, and they also can't work with infinite collections (none can).

Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
  • Everyone minds taking the size of an arbitrary collection because it forces lazy data structures. Even if it `hasDefiniteSize`, usually avoid unless you know it's cheap. – som-snytt Sep 22 '15 at 03:45
  • 1
    Uprooted because, In terms of the API, this is the most convenient. – Nadir Muzaffar Sep 22 '15 at 19:42
7

Following the OP's comment that they want to fold over it, here's a slightly different take on it that avoids calculating the length of the sequence first.

Define an iterator that will iterate over the rotated sequence

class RotatedIterator[A](seq: Seq[A], start: Int) extends Iterator[A] {
  var (before, after) = seq.splitAt(start)
  def next = after match {
    case Seq()  =>
      val (h :: t) = before; before = t; h
    case h :: t => after = t; h
  }
  def hasNext = after.nonEmpty || before.nonEmpty
}

And use it like this:

val seq = List(1, 2, 3, 4, 5)  
val xs = new RotatedIterator(seq, 2)
println(xs.toList)         //> List(3, 4, 5, 1, 2)
The Archetypal Paul
  • 41,321
  • 20
  • 104
  • 134
  • Would it be difficult to create a "rotated" `View` using a similar approach? Not asking for a implementation. – Nadir Muzaffar Sep 22 '15 at 15:48
  • I don't really know, but I suspect so. You'd need to implement a lot more operations, and integrate more deeply into the Scala collections hierarchy. It's also quite difficult to see how to implement indexed access without working out the length of the collection. – The Archetypal Paul Sep 22 '15 at 15:50
  • Accepting this answer because it's more practical because of its lazy nature. – Nadir Muzaffar Sep 22 '15 at 19:39
  • @NadirMuzaffar, or not :) (not complaining, it's your choice which answer best meets you needs) – The Archetypal Paul Aug 03 '17 at 08:34
  • No no! I could have sworn I switched it back =P. I ended up using the approach from Binzi, but in the real world, I would use yours. I'm surprised I'm allowed to switch after all this time. – Nadir Muzaffar Aug 04 '17 at 01:30
4

A simple method is to concatenate the sequence with itself and then take the slice that is required:

(seq ++ seq).slice(start, start + seq.length)

This is just a variant of the drop/take version but perhaps a little clearer.

Tim
  • 26,753
  • 2
  • 16
  • 29
3

Given:

val seq = Seq(1,2,3,4,5)

Solution:

seq.zipWithIndex.groupBy(_._2<3).values.flatMap(_.map(_._1))

or

seq.zipWithIndex.groupBy(_._2<3).values.flatten.map(_._1)

Result:

List(4, 5, 1, 2, 3)
  1. If rotation is more than length of collection - we need to use rotation%length, if negative than formula (rotation+1)%length and take absolute value.
  2. It's not efficient
Sergii Shevchyk
  • 38,716
  • 12
  • 50
  • 61
  • 1
    I see three problems: 1 - If rotate value is greater than `seq.length` then no rotation takes place. (It doesn't rotate "continuously".) 2 - `seq` is traversed at least 3 times (`zip`, `group`, `map`). 3 - Does the language guarantee the `true` values will always be extracted first? Could the next Scala release change the order `values` are retrieved? – jwvh Aug 02 '17 at 20:29
  • Thanks for comment - 1. I suppose shift%length should fix it 2. right, it's not built-in, nevertheless O(n) 3. no guarantees – Sergii Shevchyk Aug 02 '17 at 21:20
1

Another tail-recursive approach. When I benchmarked it with JMH it was about 2 times faster than solution based on drop/take:

def rotate[A](list: List[A], by: Int): List[A] = {

    @tailrec
    def go(list: List[A], n: Int, acc: List[A]): List[A] = {

      if(n > 0) {
        list match {
          case x :: xs => go(xs, n-1, x :: acc)
        }
      } else {
        list ++ acc.reverse
      }

    }

    if (by < 0) {
      go(list, -by % list.length, Nil)
    } else {
      go(list, list.length - by % list.length, Nil)
    }    
}

//rotate right
rotate(List(1,2,3,4,5,6,7,8,9,10), 3) // List(8, 9, 10, 1, 2, 3, 4, 5, 6, 7) 

//use negative number to rotate left
rotate(List(1,2,3,4,5,6,7,8,9,10), -3) // List(4, 5, 6, 7, 8, 9, 10, 1, 2, 3)
Krzysztof Atłasik
  • 21,985
  • 6
  • 54
  • 76
0

Here is one liner solution

def rotateRight(A: Array[Int], K: Int): Array[Int] = {
    if (null == A || A.size == 0) A else (A drop A.size - (K % A.size)) ++ (A take A.size - (K % A.size))
}
rotateRight(Array(1,2,3,4,5), 3)
0

Here's a fairly simple and idiomatic Scala collections way to write it:

def rotateSeq[A](seq: Seq[A], isLeft: Boolean = false, count: Int = 1): Seq[A] =
  if (isLeft)
    seq.drop(count) ++ seq.take(count)
  else
    seq.takeRight(count) ++ seq.dropRight(count)
chaotic3quilibrium
  • 5,661
  • 8
  • 53
  • 86
0

We can simply use foldLeft to reverse a list as below.

  val input = List(1,2,3,4,5)

  val res = input.foldLeft(List[Int]())((s, a) => { List(a) ++: s})

  println(res) // List(5, 4, 3, 2, 1)
syodage
  • 1
  • 1
  • Reversing a `List` is not what the question asks for. When answering questions as old as this it is always a good idea to review all the current answers before submitting a new one. – jwvh Feb 23 '19 at 03:24
0

Another one line solution if you don't need to validate the "offset":

   def rotate[T](seq: Seq[T], offset: Int): Seq[T] = Seq(seq, seq).flatten.slice(offset, offset + seq.size)
Qihong
  • 21
  • 2
  • 1
    Please see [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer) from StackOverflow Help. – coderpc Dec 20 '19 at 22:17
-1

This is a simple piece of code

  object tesing_it extends App 
{
val one = ArrayBuffer(1,2,3,4,5,6)
val  i = 2  //the number of index you want to move



 for(z<-0 to i){
   val y = 0
   var x =  one += one(y)
   x = x -= x(y)
   println("for seq after process " +z +" " + x)
  }


println(one)

}

Result:

for seq after process 0 ArrayBuffer(2, 3, 4, 5, 6, 1)

for seq after process 1 ArrayBuffer(3, 4, 5, 6, 1, 2)

for seq after process 2 ArrayBuffer(4, 5, 6, 1, 2, 3)

ArrayBuffer(4, 5, 6, 1, 2, 3)

Gujjjjuu
  • 1
  • 1