0

I want to sort a list of some objects by an attribute, but I also want to be able to specify a key when doing the sort (of those attributes). I have seen answers like this, which use the attribute to sort the objects, but I want to allow for other logic of sorting besides (for strings) just alphabetizing. Here is an example class and sort function to show what I mean:

class Pet:
    def __init__(self, name):
        self.name = name

    def __repr__(self): # for readability
        return 'Pet({})'.format(self.name)

def sort_pets(pets, reverse=False):
    pets.sort(reverse=reverse, key=lambda x : x.name)

So with this function, I can sort a list of Pet objects by their name:

>>> pets = [Pet(i) for i in ['Dexter Lewis Cumberbatch', 'Alice', 'Bozo Bob', 'Cy']]
>>> sort_pets(pets)
>>> print(pets)
[Pet(Alice), Pet(Bozo Bob), Pet(Cy), Pet(Dexter Lewis Cumberbatch)]

I can do so alphabetically or reverse alphabetically (using reverse). But I want to have options for sorting the pets (by name) with different logic (like say the name length, number of spaces, last letter, etc.). I can make a key parameter for sort_pets, but I can't pass it to the key parameter of sort, because it is already being used to access the name attribute. Is there anyway to accomplish this?

Tom
  • 8,310
  • 2
  • 16
  • 36

1 Answers1

3

You can do this by incorporating the key parameter from sort_pets into the lambda function when finding the attribute of each object:

def sort_pets(pets, reverse=False, key=None):
    if key is None:
        sortkey = lambda x : x.name
    else:
        sortkey = lambda x : key(x.name)
    pets.sort(reverse=reverse, key=sortkey)

So when a key is not passed to sort_pets, the objects are just sorted by their name attribute. If one is passed, you apply that key to each name in order to build a new lambda key function.

Now you can use the key of sort_pets to sort the Pet objects as if you were just looking at the name:

pets = [Pet(i) for i in ['Dexter Lewis Cumberbatch', 'Alice', 'Bozo Bob', 'Cy']]
print(sort_pets(pets))  #no additional sorting
print(sort_pets(pets, key=lambda x : len(x))) #sort by number of characters
print(sort_pets(pets, key=lambda x : len(x.split(' ')[0]))) #sort by length of first name
print(sort_pets(pets, key = lambda x : x[-1])) #sort by last character

Output:

[Pet(Alice), Pet(Bozo Bob), Pet(Cy), Pet(Dexter Lewis Cumberbatch)]
[Pet(Cy), Pet(Alice), Pet(Bozo Bob), Pet(Dexter Lewis Cumberbatch)]
[Pet(Cy), Pet(Bozo Bob), Pet(Alice), Pet(Dexter Lewis Cumberbatch)]
[Pet(Bozo Bob), Pet(Alice), Pet(Dexter Lewis Cumberbatch), Pet(Cy)]
Tom
  • 8,310
  • 2
  • 16
  • 36