112

I'd like to rotate a Python list by an arbitrary number of items to the right or left (the latter using a negative argument).

Something like this:

>>> l = [1,2,3,4]
>>> l.rotate(0)
[1,2,3,4]
>>> l.rotate(1)
[4,1,2,3]
>>> l.rotate(-1)
[2,3,4,1]
>>> l.rotate(4)
[1,2,3,4]

How might this be done?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • 3
    I don't use Python, but if you have a push/pop method, you can l.push(l.pop()). Then for loop it. That would cover moving forward. – Ryan Amos Feb 26 '12 at 22:29
  • Does [this question](http://stackoverflow.com/questions/2150108/efficient-way-to-shift-a-list-in-python) help? – simchona Feb 26 '12 at 22:30
  • This question seems related: http://stackoverflow.com/questions/1212025/moving-values-in-a-list-in-python – Dave Everitt Feb 26 '12 at 22:30
  • 1
    Thanks for the links to other questions. I tried searching Google and SO for `python list rotation` and didn't find any of those. This question may serve as a landing page for people thinking of 'rotating' rather than 'shifting'. – Drew Noakes Feb 26 '12 at 22:32
  • 1
    @DrewNoakes: both show up in the first page of SO search results for "python list rotate". Need to use shorter forms of the words, I think. ;-) – DSM Feb 26 '12 at 22:35
  • @DSM, I guess I expected SO's search stemming to be better than that. I'll delete this question. – Drew Noakes Feb 26 '12 at 22:38
  • 3
    The other question is specifically about efficiency; this one may cover slightly different ground. – Aaron Dufour Feb 26 '12 at 22:38
  • 1
    Deletion seems like overkill -- never hurts to have another key in the dict! -- although this question hasn't hit different ground yet (each answer has a variant in the other questions). – DSM Feb 26 '12 at 22:45
  • 4
    This "duplicate", and the answers here were more useful than the original. Thanks! – mdandr May 04 '16 at 17:13
  • For a constant space solution try, `l.insert(0, l.pop(-1))` in a for loop of n times (number of rotate). For negative rotations, `l.append(l.pop(0))` might be useful. – Mustafa Burak Cin Mar 07 '21 at 17:25

4 Answers4

199
def rotate(l, n):
    return l[-n:] + l[:-n]

More conventional direction:

def rotate(l, n):
    return l[n:] + l[:n]

Example:

example_list = [1, 2, 3, 4, 5]

rotate(example_list, 2)
# [3, 4, 5, 1, 2]

The arguments to rotate are a list and an integer denoting the shift. The function creates two new lists using slicing and returns the concatenatenation of these lists. The rotate function does not modify the input list.

Community
  • 1
  • 1
YXD
  • 31,741
  • 15
  • 75
  • 115
  • Nice and simple. It rotates the opposite direction to that specified in the question though. – Drew Noakes Feb 26 '12 at 22:36
  • 1
    @S.Lott: One could argue that the OP's direction is backwards in his example. Most Python tutorials on slice idiom with a list rotation has the sign convention the same direction as Mr E first rotation function... – the wolf Feb 26 '12 at 23:05
  • 12
    I'd add `n = n % len(n)` to make this work for `n > len(n)`. – user1071136 Nov 23 '13 at 20:31
  • Is there a way to this without `return` ? I tried `l=l[n:] + l[:n]` but when I try to return a `l` I get the original. – GinKin Mar 18 '14 at 17:44
  • 2
    Not sure what you mean. The function doesn't modify the input parameter, it returns a new list. Probably best to ask a new question about your issue. – YXD Mar 19 '14 at 15:51
  • 13
    @user1071136 I think you mean `n = n % len(l)`. – ktbiz May 03 '16 at 20:07
126

If applicable, you could use collections.deque as a solution:

import collections

d = collections.deque([1,2,3,4,5])
d.rotate(3)

print d
>>> deque([3, 4, 5, 1, 2])

As a bonus, I'd expect it to be faster than in-built list.

Matt
  • 21,026
  • 18
  • 63
  • 115
tomasz
  • 12,574
  • 4
  • 43
  • 54
  • 14
    For future readers: `collections.deque rotate()` is faster than slicing according to https://wiki.python.org/moin/TimeComplexity – Geoff Dec 16 '16 at 17:35
  • shouldn't it be mentioned that collections is rotating to the left by default? – QuestionEverything Jul 24 '17 at 03:17
  • 5
    @HasanIqbalAnik deque.rotate rotates to the right https://docs.python.org/3/library/collections.html#collections.deque.rotate – miles82 Dec 10 '17 at 20:05
28

The following function will rotate the list l, x spaces to the right:

def rotate(l, x):
  return l[-x:] + l[:-x]

Note that this will only return the original list if x is outside the range [-len(l), len(l)]. To make it work for all values of x, use:

def rotate(li, x):
  return li[-x % len(li):] + li[:-x % len(li)]
Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
Aaron Dufour
  • 17,288
  • 1
  • 47
  • 69
  • Is there a way to this without `return` ? I tried `l=l[n:] + l[:n]` but when I try to return a `l` I get the original. – GinKin Mar 18 '14 at 17:46
  • @GinKin Why without a return? That's how you return things from a function. I mean, you could use a lambda, but that just makes the return implicit. – Aaron Dufour Mar 18 '14 at 17:54
  • I want to make it 'in place' so it won't return anything and if I'll type `>>> l` after running the function I'll get a the rotated list, not the original. – GinKin Mar 18 '14 at 17:59
  • 2
    @GinKin Instead of `return ...` you can say `l[:] = ...` and it will do it in place. I don't recommend this, though. – Aaron Dufour Mar 18 '14 at 19:28
  • Why not ? (extra words for minimum amount to comment) – GinKin Mar 19 '14 at 15:53
  • 1
    @GinKin Functions in Python are generally expected to be side-effect free; normally you would only change one of the parameters if it was the first (`self`) argument to a method. It's just a convention, though. – Aaron Dufour Mar 19 '14 at 18:19
8
>>> l=[1,2,3,4]
>>> l[1:]+l[:1]
[2, 3, 4, 1]
>>> l=[1,2,3,4]
>>> l[2:]+l[:2]
[3, 4, 1, 2]
>>> l[-1:]+l[:-1]
[4, 1, 2, 3]

A general rotate n to the left (positive y in the call to rotate) or right (negative y) then:

def rotate(l, y=1):
   if len(l) == 0:
      return l
   y = y % len(l)    # Why? this works for negative y

   return l[y:] + l[:y]

If you want the direction of rotation to be the same as your example, just negate y in rotate.

def rotate(l, y=1):
   if len(l) == 0:
      return l
   y = -y % len(l)     # flip rotation direction

   return l[y:] + l[:y]
the wolf
  • 34,510
  • 13
  • 53
  • 71