views:

568

answers:

5

I have a list[] of items from which I'd like to display one randomly, but the displayed item must not repeat more than once in last x requests.

  1. list1 = item1, item2, item3, item4, item5, item6, item7, item8, item9, item 10
  2. Display a random selection from the list above
  3. list2 = store the last displayed item in list2 which should only store 7 items, not more
  4. Display a random selection from the list but make sure it doesn't exist in the list2

Is that the right way to do it? Either way, I'd like to know how to limit a list to store only 7 items?

Thanks

+4  A: 

Is this what you want?

list1 = range(10)
import random
random.shuffle(list1)
list2 = list1[:7]
for item in list2:
    print item
print list1[7]

In other words, look at random.shuffle(). If you want to keep the original list intact, you can copy it: list_copy = list1[:].

Alok
object = random.shuffle(list1) returns None, can't it store the value in an object?
Nimbuz
No, `random.shuffle()` modifies the list in-place (that's why the bit about copying). You can get a random item from a list by `random.choice()`.
Alok
+5  A: 

collections.deque is the only sequence type in python that naturally supports being bounded (and only in Python 2.6 and up.) If using python 2.6 or newer:

# Setup
from collections import deque
from random import choice
used = deque(maxlen=7)

# Now your sampling bit
item = random.choice([x for x in list1 if x not in used])
used.append(item)

If using python 2.5 or less, you can't use the maxlen argument, and will need to do one more operation to chop off the front of the deque:

while len(used) > 7:
    used.popleft()

This isn't exactly the most efficient method, but it works. If you need speed, and your objects are hashable (most immutable types), consider using a dictionary instead as your "used" list.

Also, if you only need to do this once, the random.shuffle method works too.

Crast
Good to know about deque, thanks! :)
Nimbuz
+1  A: 

Something like:

# Setup
import random
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list2 = []

# Loop for as long as you want to display items
while loopCondition:
    index = random.randint(0, len(list1)-1)
    item = list1.pop(index)

    print item

    list2.append(item)
    if(len(list2) > 7):
        list1.append(list2.pop(0))
Sapph
The *infinite loop* is only for demonstration I guess?
Felix Kling
Absolutely. Nimbuz specified that he had some number of "requests" coming in, so I just represented it as an infinite loop. I'll revise lest someone actually keep it in. :)
Sapph
+1  A: 

You could try using a generator function and call .next() whenever you need a new item.

import random
def randomizer(l, x):
    penalty_box = []
    random.shuffle(l)
    while True:
        element = l.pop(0)
        # for show
        print penalty_box, l
        yield element
        penalty_box.append(element)
        if len(penalty_box) > x:
            # penalty time over for the first element in the box
            # reinsert randomly into the list
            element = penalty_box.pop(0)
            i = random.randint(0, len(l))
            l.insert(i, element)

Usage example:

>>> r = randomizer([1,2, 3, 4, 5, 6, 7, 8], 3)
>>> r.next()
[] [1, 5, 2, 6, 4, 8, 7]
3
>>> r.next()
[3] [5, 2, 6, 4, 8, 7]
1
>>> r.next()
[3, 1] [2, 6, 4, 8, 7]
5
>>> r.next()
[3, 1, 5] [6, 4, 8, 7]
2
>>> r.next()
[1, 5, 2] [4, 3, 8, 7]
6
>>> r.next()
[5, 2, 6] [4, 3, 8, 7]
1
>>> r.next()
[2, 6, 1] [5, 3, 8, 7]
4
>>> r.next()
[6, 1, 4] [3, 8, 2, 7]
5
tkerwin
+1  A: 

I'd use set objects to get a list of items in list1 but not in list2:

import random

list1 = set(["item1", "item2", "item3", "item4", "item5",
             "item6", "item7", "item8", "item9", "item10"])
list2 = []
while True:  # Or something
    selection = random.choice(tuple(list1.difference(set(list2))))
    print(selection)
    list2.append(selection)
    if len(list2) > 7:
        list2 = list2[-7:]
Teddy