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?

Asked By: Georgia Martinez

||

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)
Answered By: coder00

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)

Answered By: Hazzu

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))
Answered By: blhsing
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.