Saturday, January 30, 2010

Cycling along a list

This question on Stack Overflow involved the problem of moving along a list and then, once you reach the end, cycling back to the beginning. One very simple way to do that is to use the % (mod) operator. If the length of the list isn't changing (and it isn't a good idea to change a list during iteration), you can cache the length of the list:

>>> L = list('abcde')
>>> N = len(L)
>>> rL = list()
>>> for i in range(12):
... rL.append(L[i%N])
...
>>> print ''.join(rL)
abcdeabcdeab

Other answers used several functions from itertools:

from itertools import cycle


We can check out the docs (remember)

$ pydoc -w itertools


>>> L = list('abcde')
>>> it = cycle(L)
>>> rL = list()
>>> for i in range(12):
... rL.append(it.next())
...
>>> print ' '.join(rL)
a b c d e a b c d e a b

To get the elements in groups of two (advancing one index at a time):

>>> it = cycle(L)
>>> x = it.next()
>>> for i in range(12):
... y = it.next()
... rL.append(x+y)
... x = y
...
>>> print ' '.join(rL)
ab bc cd de ea ab bc cd de ea ab bc

Or, you can do a full itertools solution with the functions tee and izip as well as cycle.

tee makes two independent iterators for the sequence and izip zips them up (of course):

>>> from itertools import izip, cycle, tee
>>>
>>> def pairwise(seq):
... a, b = tee(seq)
... next(b)
... return izip(a, b)
...
>>> L = list('abcde')
>>> rL = list()
>>> it = pairwise(cycle(L))
>>> for i in range(12):
... rL.append(''.join(it.next()))
...
>>> print ' '.join(rL)
ab bc cd de ea ab bc cd de ea ab bc

Or use islice:

>>> from itertools import islice
>>> it = pairwise(cycle(L))
>>> [''.join(t) for t in islice(it,6)]
['ab', 'bc', 'cd', 'de', 'ea', 'ab']