Reputation: 67
Hey guys I'm facing a problem with a for loop that I couldn't find a solution for online.
Lets say I create two classes, one for people and one for animals. By setting up a loop method for the animal class that iterates over a list of Peoples ages I wanted to find the owner for each pet which has the same age or at least the smallest age gap (this example is just for making the code look simpler). I used a min()
function in order to find the person with the smallest age gap which works fine.
But what if I added one more criteria to the selection process? For example I would like to assign the animal only to those people who own less than 3 pets, meaning that even though a person has the smallest age gap, the pet cannot be assigned to that person if it owns already 3 pets. In that case the loop would have to find the next person with the smallest age gap which owns less than 3 pets. In my case A1 would have to be assigned to P1, since it is the person with the smallest age gap which owns less than 3 pets.
Here's my code so far:
class People:
def __init__(self, name, age, pets_owned):
self.name=name
self.age=age
self.pets_owned=pets_owned
P1=People("John",16, 1)
P2=People("Alex",10, 4)
P3=People("Anna", 20, 3)
People_List=[P1, P2, P3]
People_Age=[P1.age, P2.age, P3.age]
class Animal:
def __init__(self, name, age, owner):
self.name=name
self.age=age
self.owner=owner
def find(self):
closest_age = (min(People_Age, key=lambda x: abs(x - self.age)))
for a in People_List:
if a.age ==closest_age and a.pets_owned<3:
self.owner=a.name
a.pets_owned+=1
break
elif a.age==closest_age and a.pets_owned >=3:
pass #this is where I`m stuck
print(self.owner)
A1=Animal("Snoopy",7,"not_owned_yet")
A1.find()
Upvotes: 0
Views: 110
Reputation: 10799
You can sort based on multiple attributes, by having your sorting key return a tuple. In my example, first we sort based on age gap (higher priority), then by the number of pets owned (lower priority). You don't need to use sorted
like I did, since I'm only using it to demonstrate how sorting works based on multiple attributes. You may want to use min
with the same key to get the most eligible person. You'll also want to modify assign_new_owner
to actually assign a new owner rather (using min
) than print people:
class Person:
def __init__(self, name, age, pets_owned):
self.name = name
self.age = age
self.pets_owned = pets_owned
def __str__(self):
return f"{self.name}, age {self.age} owns {self.pets_owned} pet(s)."
class Animal:
def __init__(self, name, age, owner=None):
self.name = name
self.age = age
self.owner = owner
def assign_new_owner(self, people):
sorted_people = sorted(people, key=lambda p: (abs(p.age - self.age), p.pets_owned))
for person in sorted_people:
print(person)
def main():
people = [
Person("Alex", 16, 0),
Person("Nigel", 15, 2),
Person("Fred", 10, 3),
Person("Tom", 10, 0),
Person("Tyler", 15, 0),
Person("Sam", 15, 1)
]
animal = Animal("Snoopy", 10)
animal.assign_new_owner(people)
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
Output:
Tom, age 10 owns 0 pet(s).
Fred, age 10 owns 3 pet(s).
Tyler, age 15 owns 0 pet(s).
Sam, age 15 owns 1 pet(s).
Nigel, age 15 owns 2 pet(s).
Alex, age 16 owns 0 pet(s).
EDIT: After using min, the code might look more like this:
class Person:
def __init__(self, name, age, pets_owned):
self.name = name
self.age = age
self.pets_owned = pets_owned
def __str__(self):
return f"{self.name}, age {self.age} owns {self.pets_owned} pet(s)."
class Animal:
def __init__(self, name, age, owner=None):
self.name = name
self.age = age
self.owner = owner
def __str__(self):
return f"{self.name}, age {self.age} is owned by {self.owner.name if self.owner else 'no one'}."
def assign_new_owner(self, people):
self.owner = min(people, key=lambda p: (abs(p.age - self.age), p.pets_owned))
def main():
people = [
Person("Alex", 16, 0),
Person("Nigel", 15, 2),
Person("Fred", 10, 3),
Person("Tom", 10, 0),
Person("Tyler", 15, 0),
Person("Sam", 15, 1)
]
animal = Animal("Snoopy", 10)
print(animal)
animal.assign_new_owner(people)
print(animal)
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
Output:
Snoopy, age 10 is owned by no one.
Snoopy, age 10 is owned by Tom.
Upvotes: 0
Reputation: 3120
If you know you aren't going to include certain people because of a criteria, I would pre-filter the incoming list to exclude those people. Basically, instead of using a for loop at all, just filter the list, then find the min, then add the pet.
class People:
def __init__(self, name, age, pets_owned):
self.name=name
self.age=age
self.pets_owned=pets_owned
P1=People("John",16, 1)
P2=People("Alex",10, 4)
P3=People("Anna", 20, 3)
People_List=[P1, P2, P3]
People_Age=[P1.age, P2.age, P3.age]
class Animal:
def __init__(self, name, age, owner):
self.name=name
self.age=age
self.owner=owner
def find(self):
people_with_less_than_3 = filter(lambda x: x.pets_owned<3, People_List) # filter the list to only include people that have less than 3 pets
try:
person_with_closest_age = min(people_with_less_than_3, key=lambda x: abs(x.age - self.age)) # change this to return a person as well
except:
# do something if no person with < 3 pets
self.owner = person_with_closest_age.name
print(self.owner)
A1=Animal("Snoopy",7,"not_owned_yet")
A1.find()
Upvotes: 1