monotasker
monotasker

Reputation: 2102

Python -- how to loop over range starting at random point

I have a list of categories (1-4) and I want a loop to iterate over all of them. But I need to be able to start with a random category and wrap around to the beginning of the list if my starting point was ahead of list[0].

I was able to do it in a rather verbose way, but I'm wondering if there's a faster/more elegant way. Here's what I did (and it works):

def categorize(self, cat):

    cats = [1,2,3,4]
    if cat > 1: 
        ncats = cats[:(cat-1)]
        cats = cats[(cat-1):]
        cats.extend(ncats)

    for c in cats:
        pass

Upvotes: 4

Views: 2771

Answers (10)

D_Bye
D_Bye

Reputation: 899

import random
cats = [1,2,3,4]

def cycle_cats(items, start):
    sorted_items = items[items[start]:] + items[:items[start]]
    for item in sorted_items:
        yield item

for cat in cycle_cats(cats, random.choice(cats)):
    print cat

Preserves the order, doesn't care about the length of the list of cats, and expects the caller to specify the entry point.

Upvotes: 0

Paulo Scardine
Paulo Scardine

Reputation: 77251

>>> cats = [1,2,3,4]
>>> import random
>>> random.shuffle(cats)
>>> cats
[1, 3, 4, 2]
>>> random.shuffle(cats)
>>> cats
[1, 4, 3, 2]

[ updated ]

>>> def circle_iter(items, start=0):
...     l = len(items)
...     for i in items:
...         yield items[start]
...         start += 1
...         if start == l: start = 0
...
>>> cats = [ 1, 2, 3, 4 ]
>>> for cat in circle_iter(cats, 2): print cat
...
3
4
1
2
>>> for cat in circle_iter(cats, 1): print cat
...
2
3
4
1

Upvotes: 0

Don Question
Don Question

Reputation: 11614

For a generic warp/round-robin solution i propose something like:

from itertools import cycle:
from random import choice

cats = [1,2,3,4]
def warp(iterable, start):
    c = cycle(iterable)
    while c.next() is not start: pass
    for counter in range(len(iterable)):
        yield c.next()

#random start part:
for cat in warp(cats, choice(cats)):
    print cat

The type of the iterable-items doesn't matter and you don't have to check for an index-number, but just use the item itself!

Upvotes: 1

fraxel
fraxel

Reputation: 35269

This should do it:

import random
cats = [1,2,3,4]
start = random.choice(cats)
new_order_cats = cats[cats.index(start):] + cats[:cats.index(start)]
print 'new order', new_order_cats

Upvotes: 0

YXD
YXD

Reputation: 32511

from random import randrange
cats = [1,2,3,4]
i = randrange(len(cats))

for c in cats[i:]+cats[:i]:
   pass

(Changed choice to randrange as per suggestion)

Upvotes: 8

Gandi
Gandi

Reputation: 3652

The general idea is:

>>> cats = [1, 2, 3, 4]
>>> import random
>>> r = random.randrange(len(cats))
>>> for i in range(len(cats)):
...     current = cats[(r+i)%len(cats)]
...     print current
...
3
4
1
2

Upvotes: 3

somewhatoff
somewhatoff

Reputation: 981

Does the original list need to remain in order? Because otherwise you can use random.shuffle to randomise it in place:

cats = [1,2,3,4]
import random
random.shuffle(cats)
# Cats will now be in random order and can be looped normally.

Upvotes: 1

DorkRawk
DorkRawk

Reputation: 770

Use the modulo operator!

import random

cats = [1,2,3,4]

i = random.randint(0,3)

for n in range(len(cats)):
    print cats[i%len(cats)]
    i+=1

Upvotes: 0

Silas Ray
Silas Ray

Reputation: 26150

Well, you could reduce it down to cats = cats[cat - 1:] + cats[:cat - 1]

Or build a custom data structure that overrides iter so that it will loop around your list once and only once from any arbitrary point.

Upvotes: 3

Yuriy Zubarev
Yuriy Zubarev

Reputation: 2871

from random import shuffle
shuffle(cats)
for c in cats:
    loop expressions here

Upvotes: 0

Related Questions