Reputation: 2102
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
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
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
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
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
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
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
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
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
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
Reputation: 2871
from random import shuffle
shuffle(cats)
for c in cats:
loop expressions here
Upvotes: 0