Rotating lists?

I

Ivan Voras

I need to transform this:

[1,2,3]

into this:

[2,3,1]

(a left-rotation. Actually, any rotation will do).

I tried:

a = a[1:] + a[0]

which doesn't work because there's no __add__ between a list and
integer, and:

a = a[1:].append(a[0])

but it doesn't work, since append returns None :( Right now, I'm doing
it with a temporary variable and it looks ugly - is there an elegant way
of doing it?
 
R

Raymond Hettinger

[Ivan Voras]
I need to transform this:

[1,2,3]

into this:

[2,3,1]

(a left-rotation. Actually, any rotation will do).

For Py2.4, use collections.deque.rotate().

For earlier Pythons, concatenate list slices: a[1:] + a[:1]



Raymond Hettinger
 
P

Peter Hansen

Ivan said:
I need to transform this:
[1,2,3]
into this:
[2,3,1]

Right now, I'm doing
it with a temporary variable and it looks ugly - is there an elegant way
of doing it?
>>> l = range(1, 4)
>>> l [1, 2, 3]
>>> import itertools
>>> list(itertools.islice(itertools.cycle(l), 1, len(l)+1))
[2, 3, 1]

That is, build an iterator that cycles over l indefinitely,
then take a slice of it starting x items in (where x==1 in
this case) and continuing until you've retrieved a number
of elements equal to the length of the original sequence.

-Peter
 
I

Irmen de Jong

Ivan said:
I need to transform this:

[1,2,3]

into this:

[2,3,1]

(a left-rotation. Actually, any rotation will do).

I tried:

a = a[1:] + a[0]

which doesn't work because there's no __add__ between a list and
integer, and:

a = a[1:].append(a[0])

but it doesn't work, since append returns None :( Right now, I'm doing
it with a temporary variable and it looks ugly - is there an elegant way
of doing it?

This smells like a use-case for itertools.cycle:
>>> import itertools
>>> c=itertools.cycle( [1,2,3] )
>>> c.next() 1
>>> c.next() 2
>>> c.next() 3
>>> c.next() 1
>>> c.next()
2

.... or is this not what you need to rotate your lists for?

--Irmen de Jong
 
P

Paul Rubin

Ivan Voras said:
I tried:

a = a[1:] + a[0]

which doesn't work because there's no __add__ between a list and
integer, and:

You meant to say

a = a[1:] + [a[0]]
 
T

Thorsten Kampe

* Ivan Voras (2004-09-16 00:10 +0200)
I need to transform this:

[1,2,3]

into this:

[2,3,1]

(a left-rotation. Actually, any rotation will do).

I tried:

a = a[1:] + a[0]

which doesn't work because there's no __add__ between a list and
integer, and:

a = a[1:].append(a[0])

but it doesn't work, since append returns None :( Right now, I'm doing
it with a temporary variable and it looks ugly - is there an elegant way
of doing it?

def rotate(seq,
offset):
""" shift seq to the left by offset, with the elements shifted off the
beginning inserted back at the end """
return seq[offset:] + seq[:eek:ffset]
 
J

Jim Sizelove

Thorsten said:
def rotate(seq,
offset):
""" shift seq to the left by offset, with the elements shifted off the
beginning inserted back at the end """
return seq[offset:] + seq[:eek:ffset]


You can also use a generator function:

def rotator(seq, offset=1):
while True:
yield seq
seq = seq[offset:] + seq[:eek:ffset]

T = rotator(range(5))
T.next()
[0, 1, 2, 3, 4]
T.next()
[1, 2, 3, 4, 0]
T.next()
[2, 3, 4, 0, 1]

You can rotate to the right using a negative offset:
L = rotator( range(5), -1 )
L.next()
[0, 1, 2, 3, 4]
L.next()
[4, 0, 1, 2, 3]
L.next()
[3, 4, 0, 1, 2]
 
I

Ivan Voras

Irmen said:
This smells like a use-case for itertools.cycle:
import itertools
c=itertools.cycle( [1,2,3] )
c.next() 1
c.next() 2
c.next() 3
c.next() 1
c.next()
2

... or is this not what you need to rotate your lists for?

No, I don't need an (infinite) cycle, just the rotated list. But thanks,
I didn't notice itertools before :)
 
R

Raymond Hettinger

[Ivan Voras]> > a = a[1:] + a[0]
[Paul Rubin]
You meant to say

a = a[1:] + [a[0]]

A better answer is a[1:] + a[:1] which nicely handles lists of length 0 and
length 1 as well as the OP's original example.

The advantage of using slices to extract a single element is that it avoids
IndexErrors for empty lists. This technique comes up often enough that I
thought it worth pointing out here.

Also worth mentioning is that slicing and concatenation are supported by several
sequence types. So, this operation can be abstracted to apply to more than just
lists.

It might also be opportune to point out the virtues of writing a few doctests
that would have surfaced the issues immediately.

Of course, Paul already knows this. This note is for the people who don't.


Raymond Hettinger


def rotate(seq):
""" Rotate the first element to the end of a sequence.

Returns a new sequence of the same type.
The type must support slicing and concatenation.
>>> rotate(range(5)) [1, 2, 3, 4, 0]
>>> rotate(range(1)) [0]
>>> rotate(range(0)) []
>>> rotate('abc') 'bca'
>>> rotate('') ''
>>> rotate(tuple('abc')) ('b', 'c', 'a')
>>> rotate(tuple(''))
()
"""

return seq[1:] + seq[:1]

import doctest
doctest.testmod()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top