Sorting a list of objects by multiple attributes where the user chooses which attributes to prioritize
Question:
I am trying to sort a list containing the following object by its attributes:
@dataclass
class Room():
house_num: int
room_num: int
view: str
I figured out how to sort by multiple attributes with the following line:
all_rooms.sort(key=lambda r: (r.house_num, r.room_num, r.view))
But I want the user to be able to specify an order for prioritizing the attributes in a config file. For example, if the config file said to prioritize view first, house number second, and room number last, the above line would look like this instead:
all_rooms.sort(key=lambda r: (r.view, r.house_num, r.room_num))
But I’m not sure how to achieve this. I don’t want the user to have to manually change the ordering inside sort(). The current config file I have is a YAML file and would probably have it look like this:
priority: [view, house_num, room_num]
Any suggestions?
Answers:
You can use vars
as follows
from functools import partial
class Room:
def __init__(self, house_num, room_num, view):
self.house_num = house_num
self.room_num = room_num
self.view = view
def __str__(self):
return "{0.room_num} {0.house_num} {0.view}".format(self)
room1 = Room(3, 1, "a")
room2 = Room(2, 1, "b")
rooms = [room1, room2]
def sort_(r, priority):
vals = []
for p in priority:
vals.append(vars(r)[p])
return tuple(vals)
priority = ['view', 'house_num', 'room_num']
sorter = partial(sort_, priority=priority)
for room in sorted(rooms, key=sorter):
print(room)
Instead of using lambda
you can create a function and use getattr
to get attribute values in preferred order:
priority = ['view', 'house_num', 'room_num']
def evaluate(room):
values = [getattr(room, p) for p in priority]
return tuple(values)
all_rooms.sort(key=evaluate)
This is where operator.attrgetter
would fit right in:
from operator import attrgetter
priority = ['view', 'house_num', 'room_num']
all_rooms.sort(key=attrgetter(*priority))
I am trying to sort a list containing the following object by its attributes:
@dataclass
class Room():
house_num: int
room_num: int
view: str
I figured out how to sort by multiple attributes with the following line:
all_rooms.sort(key=lambda r: (r.house_num, r.room_num, r.view))
But I want the user to be able to specify an order for prioritizing the attributes in a config file. For example, if the config file said to prioritize view first, house number second, and room number last, the above line would look like this instead:
all_rooms.sort(key=lambda r: (r.view, r.house_num, r.room_num))
But I’m not sure how to achieve this. I don’t want the user to have to manually change the ordering inside sort(). The current config file I have is a YAML file and would probably have it look like this:
priority: [view, house_num, room_num]
Any suggestions?
You can use vars
as follows
from functools import partial
class Room:
def __init__(self, house_num, room_num, view):
self.house_num = house_num
self.room_num = room_num
self.view = view
def __str__(self):
return "{0.room_num} {0.house_num} {0.view}".format(self)
room1 = Room(3, 1, "a")
room2 = Room(2, 1, "b")
rooms = [room1, room2]
def sort_(r, priority):
vals = []
for p in priority:
vals.append(vars(r)[p])
return tuple(vals)
priority = ['view', 'house_num', 'room_num']
sorter = partial(sort_, priority=priority)
for room in sorted(rooms, key=sorter):
print(room)
Instead of using lambda
you can create a function and use getattr
to get attribute values in preferred order:
priority = ['view', 'house_num', 'room_num']
def evaluate(room):
values = [getattr(room, p) for p in priority]
return tuple(values)
all_rooms.sort(key=evaluate)
This is where operator.attrgetter
would fit right in:
from operator import attrgetter
priority = ['view', 'house_num', 'room_num']
all_rooms.sort(key=attrgetter(*priority))