My question is similar to this one posted a few years back, but I have one additional thing that I do not understand how to do. Say I have two lists, each holding a set of objects. These objects share a common attribute:
class Student:
def __init__(self, value) -> None:
self.value = value
class School:
def __init__(self, value) -> None:
self.value = value
self.students = []
Student_List = [Student(1), Student(1), Student(3), Student(3), Student(5)]
School_List = [School(5), School(2), School(1), School(3), School(4)]
My goal is to search for objects with matching values and then perform operations on the objects held within the lists. The current method I have is as follows:
for student in Student_List:
for school in School_List:
if student.value == school.value:
school.students.append(student)
break
This is obviously very inefficient, and finding an intersection would be much easier. However, notice how I am altering the attributes of School by appending the Student object to the School's list.
CodePudding user response:
You can create a reverse mapping that maps each school value to the corresponding school object, so that you can iterate through the list of students and efficiently obtain the matching school object with the value mapping to append the student object to:
schools = {school.value: school for school in School_List}
for student in Student_List:
schools[student.value].students.append(student)
With this approach you can reduce the time complexity from O(n ^ 2) to O(n).
Demo: https://replit.com/@blhsing/ImpracticalSimultaneousLock
CodePudding user response:
For this specific example, I believe you could do it this way:
for school in School_List:
school.students.extend([s for s in Student_List if s.value == school.value])
CodePudding user response:
What we need is a a quick way to look up the school, given a value. For that, I create a dictionary called school_dict
:
class Student:
def __init__(self, value) -> None:
self.value = value
def __repr__(self):
return f"Student({self.value})"
class School:
def __init__(self, value) -> None:
self.value = value
self.students = []
def __repr__(self):
return f"School(value={self.value}, students={self.students})"
Student_List = [Student(1), Student(1), Student(3), Student(3), Student(5)]
School_List = [School(5), School(2), School(1), School(3), School(4)]
school_dict = {s.value: s for s in School_List}
for student in Student_List:
value = student.value
try:
school_dict[value].students.append(student)
except KeyError:
pass
School_list
now is:
[
School(value=5, students=[Student(5)]),
School(value=2, students=[]),
School(value=1, students=[Student(1), Student(1)]),
School(value=3, students=[Student(3), Student(3)]),
School(value=4, students=[])
]
Notes
school_dict
allows quick look up to a school object, given a value- The
try/except
construct guards against those student values that do not have corresponding school.