You will not miss an occasion in your software engineering career where you will be needed to sort a list of things doesn’t matter it could be numbers, strings, or objects. I once encountered a problem that I was needed to sort a list of objects. Here is a good example of list of employees(Employee has 3 attributes of name, staff number and salary) that we are required to sort. We can sort by name, staff number or salary.
employees = [
Employee('Dave', 173, 122000),
Employee('Dyke', 172, 122000),
Employee('Jake', 124, 142000),
Employee('John', 143, 95000),
Employee('Jane', 193, 100000),
]
While working on the solution of this kind of problem I ended up with two solutions. My initial solution had two loops and each loop had a nested loop which I guess was slow and less readable. After googling around to find efficient and more readable approach I landed into python documentation. I came across a cleaner way to sort the objects not only of a list but even of tuples. You can sort a list in python using sorted(iterable, key=None, reverse=False) or list.sort(). This article applies sorted for sorting the above problem. Sorted is a python in-built function you do not need to import it.
Let us begin by a simple example of sorting a list of numbers or strings. In order to sort values in ascending order you are only needed to pass the iterables to the sorted function. You can either pass a keyword argument reverse=False or leave it out.
sorted([5, 1, 2, 4, 3]) # returns [1, 2, 3, 4, 5]
sorted([5, 1, 2, 4, 3], reverse=False) # returns [1, 2, 3, 4, 5]
In order to sort values in a descending order we are required to pass reverse=True as a second argument to the sorted function.
sorted([5, 1, 2, 4, 3], reverse=True) # returns [5, 4, 3, 2, 1]
Sorted returns a new sorted list from the items in the iterable. The function takes three arguments, key and reverse which must be specified as keyword arguments are optional as shown in the code above. According to the python documentation, the key specifies a function of one argument that is used to extract a comparison key from each element in iterable. For example we need to change every list value to lower while comparing key=str.lower. While reverse is a boolean value that if set to True, then the list of elements will be sorted as if each comparison were reversed(descending).
Now, how do we sort a list of objects given above based on one or more attributes of the object. We will begin by defining a class of that object can be created from. Since python does have operator module that offers us with itemgetter(), attrgetter(), and a methodcaller() that will be helpful for us
class Employee:
def __init__(self, name, staff_no, salary):
self.name = name
self.staff_no = staff_no
self.salary = salary
def __repr__(self):
return repr((self.name, self.staff_no, self.salary))
if __name__ == '__main__':
employees = [
Employee('Dave', 173, 122000),
Employee('Dyke', 172, 122000),
Employee('Jake', 124, 142000),
Employee('John', 143, 95000),
Employee('Jane', 193, 100000),
]
Sorting by one attribute(e.g. salary) in ascending order
employees = sorted(employees, key=lambda employee:employee.salary)
# returns [('John', 143, 95000), ('Jane', 193, 100000), ('Dave', 173, 122000), ('Dyke', 172, 122000), ('Jake', 124, 142000)]
Sorting by one attribute(e.g. salary) in descending order
employees = sorted(employees, key=lambda employee:employee.salary, reverse=True)
# returns[('Jake', 124, 142000), ('Dave', 173, 122000), ('Dyke', 172, 122000), ('Jane', 193, 100000), ('John', 143, 95000)]
In order to sort by more than one attributes we use attrgetter(attr_name_1, attr_name_2) as key value. It means if the first attribute values are equal we then compare using the values of the second attribute.
employees = sorted(employees, key=attrgetter('salary', 'staff_no'))
# [('John', 143, 95000), ('Jane', 193, 100000), ('Dyke', 172, 122000), ('Dave', 173, 122000), ('Jake', 124, 142000)]
In stead of using the actual attribute we can using the index position of an attribute in the object tuple.
employees = [
('Dave', 173, 122000),
('Dyke', 172, 122000),
('Josh', 172, 130000),
('Jake', 124, 142000),
('John', 143, 95000),
('Jane', 193, 100000),
]
sorted(employees, key=itemgetter(1,2)) //sort by staff_number first, then salary
# [('Jake', 124, 142000), ('John', 143, 95000), ('Dyke', 172, 122000), ('Josh', 172, 130000), ('Dave', 173, 122000), ('Jane', 193, 100000)]
Thank you for your feedback.
Top comments (0)