23

Say I have a list:

l = [1, 2, 3, 4]

And I want to cycle through it. Normally, it would do something like this,

1, 2, 3, 4, 1, 2, 3, 4, 1, 2...

I want to be able to start at a certain point in the cycle, not necessarily an index, but perhaps matching an element. Say I wanted to start at whatever element in the list ==4, then the output would be,

4, 1, 2, 3, 4, 1, 2, 3, 4, 1...

How can I accomplish this?

john
  • 3,043
  • 5
  • 27
  • 48

7 Answers7

26

Look at itertools module. It provides all the necessary functionality.

from itertools import cycle, islice, dropwhile

L = [1, 2, 3, 4]

cycled = cycle(L)  # cycle thorugh the list 'L'
skipped = dropwhile(lambda x: x != 4, cycled)  # drop the values until x==4
sliced = islice(skipped, None, 10)  # take the first 10 values

result = list(sliced)  # create a list from iterator
print(result)

Output:

[4, 1, 2, 3, 4, 1, 2, 3, 4, 1]
ovgolovin
  • 13,063
  • 6
  • 47
  • 78
  • `dropwhile` doesn't seem necessary here. You can instead use `list(islice(cycled, L.index(4), L.index(4) + 9))` – busybear Jan 27 '21 at 02:29
17

Use the arithmetic mod operator. Suppose you're starting from position k, then k should be updated like this:

k = (k + 1) % len(l)

If you want to start from a certain element, not index, you can always look it up like k = l.index(x) where x is the desired item.

Stefano Munarini
  • 2,711
  • 2
  • 22
  • 26
Sufian Latif
  • 13,086
  • 3
  • 33
  • 70
4

I'm not such a big fan of importing modules when you can do things by your own in a couple of lines. Here's my solution without imports:

def cycle(my_list, start_at=None):
    start_at = 0 if start_at is None else my_list.index(start_at)
    while True:
        yield my_list[start_at]
        start_at = (start_at + 1) % len(my_list)

This will return an (infinite) iterator looping your list. To get the next element in the cycle you must use the next statement:

>>> it1 = cycle([101,102,103,104])
>>> next(it1), next(it1), next(it1), next(it1), next(it1)
(101, 102, 103, 104, 101) # and so on ...
>>> it1 = cycle([101,102,103,104], start_at=103)
>>> next(it1), next(it1), next(it1), next(it1), next(it1)
(103, 104, 101, 102, 103) # and so on ...
juliomalegria
  • 24,229
  • 14
  • 73
  • 89
2
import itertools as it
l = [1, 2, 3, 4]
list(it.islice(it.dropwhile(lambda x: x != 4, it.cycle(l)),  10))
# returns: [4, 1, 2, 3, 4, 1, 2, 3, 4, 1]

so the iterator you want is:

it.dropwhile(lambda x: x != 4, it.cycle(l))
eumiro
  • 207,213
  • 34
  • 299
  • 261
  • @gnibbler It would take to put `4` in parenthesis `(4).__cmp__`. Otherwise it doesn't work (at least in Python 2.7.2). And with parenthesis it doesn't look that beautiful. – ovgolovin Jan 20 '12 at 12:02
  • @gnibbler And as of Python 3 it would take to use `__eq__` instead of `__cmp__` (there is no `__cmp__` as of the version 3). – ovgolovin Jan 20 '12 at 12:06
  • @ovgolovin What @gnibbler wrote works without parentheses. Did you miss the space between `4` and `.`? – Duncan Jan 20 '12 at 13:54
  • @ovgolovin for Python 3 it should be `__ne__` or `__gt__` rather than `__eq__`. – Duncan Jan 20 '12 at 14:01
  • 2
    space between 4 and . ? what the heck is going on there? – wim Jan 20 '12 at 14:36
  • 8
    @wim, if you write `4.__cmp__` Python will parse it as a floating point number `4.` followed by an identifier `__cmp__` and that's a syntax error. `4 .__cmp__` on the other hand is the integer `4` followed by a period to indicate an attribute reference and the attribute `__cmp__`. – Duncan Jan 20 '12 at 15:47
1

Hm, http://docs.python.org/library/itertools.html#itertools.cycle doesn't have such a start element.

Maybe you just start the cycle anyway and drop the first elements that you don't like.

0

Another weird option is that cycling through lists can be accomplished backwards. For instance:

# Run this once
myList = ['foo', 'bar', 'baz', 'boom']
myItem = 'baz'

# Run this repeatedly to cycle through the list
if myItem in myList:
    myItem = myList[myList.index(myItem)-1]
    print myItem
0

Can use something like this:

def my_cycle(data, start=None):
  k = 0 if not start else start
  while True:
    yield data[k]
    k = (k + 1) % len(data)

Then run:

for val in my_cycle([0,1,2,3], 2):
  print(val)

Essentially the same as one of the previous answers. My bad.

stephanmg
  • 746
  • 6
  • 17