Assign equal number chores to each person (assertion made elsewhere to ensure chores % number of people == 0) with the stipulation that each chore must not be one which the person was assigned the previous time.
My simpleton solution (there has to be a cleverer way):
import random
chores = ["hoovering", "dusting", "wash dishes", "tidy garden",
"clean windows", "empty dishwasher", "dust", "cook"]
people_data = {"David": {"email": "[email protected]",
"chores" : []},
"Mark": {"email": "[email protected]",
"chores": []},
"Anaya": {"email": "[email protected]",
"chores": []},
"Cooper": {"email": "[email protected]",
"chores": []}
}
prev_assignment_data = None
def assign_chores(chore_list, prev_assignment_data, people_data):
"""Returns a dict with assign chores to each person
Arguments:
chore_list -- a list of the chores to be assigned
prev_assignment_data -- if there has been prev chore assignments this
will be a dict containing said assignments.
Otherwise, it will have no value ('None')
people_data -- a dict of the names and the email address
associated with those names. Assigned chores
will be appended to this dict and dict
returned.
"""
# if no previous data available, assign each a random chore
if not prev_assignment_data:
while chore_list:
for name in people_data.keys():
chore = random.choice(chore_list)
chore_list.remove(chore)
people_data[name]["chores"].append(chore)
# if prev data available, assign each chores not the same as previous
else:
new_chores = False
while not new_chores:
chore_list_copy = chore_list
for name in people_data.keys():
# get all chores still available and which are not those
# the person was assigned the previous time
avail_chores = [chore for chore in chore_list_copy if chore not
in prev_assignment_data[name]["chores"]]
if avail_chores:
chore = random.choice(avail_chores)
people_data[name]["chores"].append(chore)
chore_list_copy.remove(chore)
# if only chores remaining are those the person did previously,
# restart entire assignment process
else:
break
# all chores assigned and are different to those person did prev
if not chore_list_copy:
new_chores = True
return people_data
assigned_chores = assign_chores(chores, prev_assignment_data,
people_data)
print(assigned_chores)
1 Answer 1
Well, you have your code in a function, but it is still top down. It makes it harder to read, and much harder to improve.
If you instead break the code down into separate functions you'll have an easier time improving code. You'll also not repeat your self.
Make use of annotations!
def assign_chores(chore: list, prev_assignment: dict or None, persons: dict):
...
that way you don't have to litter your variable names with clues to what types they might be.
I think your trying to create a deep copy and not a shallow copy:
chore_list_copy = chore_list
to make a deep copy;
chore_list_copy = chore_list[:]
that way you'll actually get a copy.
for name in people_data.keys():
is the same as
for name in people_data:
Instead of working with the objects in people_data i suggest you work with data structures. It becomes easier to manipulate that way.
The data you care about can be represented with:
chores_dist = [[] for _ in range(len(persons))]
and if previous is summited:
prev_chores = [person["chores"] for person in prev_assigment.values()]
Work with data structures instead.
Protect your code by:
def main():
...
if __name__ == '__main__':
main()
that way your code don't get executed if you import your code into another file.
Instead of choosing a random chore, you could just shuffle them all beforehand, that way the randomness task is done.
chores = chores[:]
shuffle(chores)
Another way to call your function then to send in a None
def assign_chores(chore_list, prev_assignment_data, people_data):
...
prev_assignment_data = None
assigned_chores = assign_chores(chore_list, prev_assignment_data,
people_data)
is to define it in your function
def assign_chore(chores: list, persons: dict, prev_assignment: dict=None):
...
now you can call it like
people_data = assign_chore(chores, people_data)
people_data = assign_chore(chores, people_data, some_previous_data)
When I broke down your code I did it like this:
from random import shuffle
def assign(chores: list, persons: dict):
for key, person in enumerate(persons):
persons[person]["chores"] = chores[key]
return persons
def get_shuffled_chores(chores: list, persons: dict):
chores = chores[:]
shuffle(chores)
chores_dist = [[] for _ in range(len(persons))]
for key, i in enumerate(range(len(chores))):
chores_dist[key % len(persons)] += [chores.pop()]
return chores_dist
def assign_without_regard(chores: list, persons: dict):
return assign(get_shuffled_chores(chores, persons), persons)
def is_repetitive(news: list, olds: list):
for new, old in zip(news, olds):
if any(chore in old for chore in new):
return False
return True
def assign_with_regard(chores: list, prev_assigment: dict, persons: dict):
prev_chores = [person["chores"] for person in prev_assigment.values()]
new_chores = get_shuffled_chores(chores, persons)
return assign(new_chores, persons) \
if not is_repetitive(new_chores, prev_chores) \
else assign_chore(chores, prev_assigment, persons)
def assign_chore(chores: list, persons: dict, prev_assignment: dict=None):
if prev_assignment is None:
return assign_without_regard(chores, persons)
return assign_with_regard(chores, prev_assignment, persons)
def main():
chores = ["hoovering", "dusting", "wash dishes", "tidy garden",
"clean windows", "empty dishwasher", "dust", "cook"]
people_data = {"David": {"email": "[email protected]",
"chores": []},
"Mark": {"email": "[email protected]",
"chores": []},
"Anaya": {"email": "[email protected]",
"chores": []},
"Cooper": {"email": "[email protected]",
"chores": []}
}
people_data = assign_chore(chores, people_data)
print(people_data)
print(assign_chore(chores, people_data, people_data))
if __name__ == '__main__':
main()