Reputation: 33
I'm trying to make a secret santa programm. The input is in form of the list of names of people g. ["John", "Bob", "Alice"] and the list of emials ["[email protected]", "[email protected]", "[email protected]"]. I need to generate pairs of email adress and a random name which doesn't belong to the said email adress. For this I have written the function compare.
def compare(list_of_names, list_of_emails):
zipped_lists = zip(list_of_emails, list_of_names)
random.shuffle(list_of_emails)
zipped_shuffled_lists = zip(list_of_emails, list_of_names)
for pair in zipped_lists:
for shuffle_pair in zipped_shuffled_lists:
if shuffle_pair == pair:
return compare(list_of_names, list_of_emails)
return zipped_shuffled_lists
But instead of shuffling like it should it just creates a recursion. i still can't find out why. After a finite amount of time it should create two different lists that work. Also the shuffled_list_of_emails is not iterable, why?
EDIT:changed the code with shuffle because it works in place
Upvotes: 1
Views: 68
Reputation: 3790
There's correct answer from @ForceBru already. But a will contribute a little.
You should avoid zip's
lazy evaluation and unfold zips with, for example, list
:
def compare(list_of_names, list_of_emails):
zipped_lists = list(zip(list_of_emails, list_of_names)) # eager evaluation instead of lazy
random.shuffle(list_of_emails) # shuffle lists
zipped_shuffled_lists = list(zip(list_of_emails, list_of_names)) # eager again
for pair in zipped_lists:
for shuffle_pair in zipped_shuffled_lists:
if shuffle_pair == pair:
return compare(list_of_names, list_of_emails)
return zipped_shuffled_lists
But I guess you need no recursion and can achieve your task easier:
def compare(list_of_names, list_of_emails):
zipped_lists = list(zip(list_of_emails, list_of_names))
random.shuffle(zipped_lists) # shuffle list of emails and names
result = []
shuffled_emails = [i[0] for i in zipped_lists]
for i, _ in enumerate(shuffled_emails):
result.append(zipped_lists[i-1][1]) # shift email relatively one position to the right
return list(zip(result, shuffled_emails))
This code links an name with an email of a previous name, which is randomly selected, and it guaranteed does not match. There's no recursion, works fine for lists with two or more elements.
Upvotes: 0
Reputation: 44878
zip
is lazy!I'm not sure why, but I'm too excited about this right now, so the answer might be a bit messy. Feel free to ask for clarification)
Let's step through your code:
def compare(list_of_names, list_of_emails):
# the `zip` object doesn't actually iterate over any of its arguments until you attempt to iterate over `zipped_lists`
zipped_lists = zip(list_of_emails, list_of_names)
# modify this IN-PLACE; but the `zip` object above has a pointer to this SAME list
random.shuffle(list_of_emails)
# since the very first `zip` object has `list_of_emails` as its argument, AND SO DOES THE ONE BELOW, they both point to the very same, SHUFFLED (!) list
zipped_shuffled_lists = zip(list_of_emails, list_of_names)
# now you're iterating over identical `zip` objects
for pair in zipped_lists:
for shuffle_pair in zipped_shuffled_lists:
# obviously, this is always true
if shuffle_pair == pair:
# say "hello" to infinite recursion, then!
return compare(list_of_names, list_of_emails)
return zipped_shuffled_lists
>>> List = list(range(5))
>>> List
[0, 1, 2, 3, 4]
>>> zipped_1 = zip(List, range(5))
>>> import random
>>> random.shuffle(List)
>>> zipped_2 = zip(List, range(5))
>>> print(List)
[4, 2, 3, 0, 1]
>>> zipped_1, zipped_2 = list(zipped_1), list(zipped_2)
>>> zipped_1 == zipped_2
True
You see, two different zip
objects applied to the same list at different times (before and after that list is modified in-place) produce the exact same result! Because zip
doesn't do the zipping once you do zip(a, b)
, it will produce the zipped... uh, stuff... on-the-fly, while you're iterating over it!
So, to fix the issue, do not shuffle the original list, shuffle its copy:
list_of_emails_copy = list_of_emails.copy()
random.shuffle(list_of_emails_copy)
zipped_shuffled_lists = zip(list_of_emails_copy, list_of_names)
Upvotes: 1