Sorting

Python provides two built-in functions for sorting:

  • list.sort(): modifies the list in-place
  • sorted(): builds a new sorted list from an iterable

Difference between these methods:

  • list.sort() method is only defined for lists. In contrast, the sorted() function accepts any iterable.
  • list.sort() modifies the list in-place and returns None. sorted() returns a new sorted list. Usually sort() is less convenient than sorted().

Sorting Basics

A simple ascending sort is very easy: just call the sorted() function. It returns a new sorted list.

Example

>>> a = [5, 2, 1, 3, 4]
>>> b = sorted(a)

>>> a # the original list is not affected
[5, 2, 1, 3, 4]

>>> b
[1, 2, 3, 4, 5]

Key

Both list.sort() and sorted() have a key parameter.

The value of the key parameter should be a function (or other callable) that serves as a key for the sort comparison. In other word, key decides the comparison criteria for sorting.

One way to specify key is to use the lambda function.

Example:

students_dict = [
    {"name": "John", "age": 12, "grade": "A"},
    {"name": "Mary", "age": 14, "grade": "B"},
    {"name": "Ben", "age": 13, "grade": "A"}
]

sorted(students_dict, key=lambda student: student.get("age")) # sort by age

Output:

[{'age': 12, 'grade': 'A', 'name': 'John'},
 {'age': 13, 'grade': 'A', 'name': 'Ben'},
 {'age': 14, 'grade': 'B', 'name': 'Mary'}]

This also works for objects with named attributes.

Example:

class Student:
    def __init__(self, name, age, grade):
        self.name = name
        self.age = age
        self.grade = grade
    
    def __repr__(self):
        return repr((self.name, self.age, self.grade))

students = [
    Student("John", 12, "A"),
    Student("Mary", 14, "B"),
    Student("Ben", 13, "A")
]
sorted(students, key=lambda student: student.age)

Output:

[('John', 12, 'A'), ('Ben', 13, 'A'), ('Mary', 14, 'B')]

Operator Module Functions

Python operator module provides convenience functions to make accessor functions easier and faster:

  • itemgetter()
  • attrgetter()
  • methodcaller()

itemgetter()

Example:

students_tuple = [
    ("John", 12, "A"),
    ("Mary", 14, "B"),
    ("Ben", 13, "A")            
]

sorted(students_tuple, key=itemgetter(1))

Output:

[('John', 12, 'A'), ('Ben', 13, 'A'), ('Mary', 14, 'B')]
students_dict = [
    {"name": "John", "age": 12, "grade": "A"},
    {"name": "Mary", "age": 14, "grade": "B"},
    {"name": "Ben", "age": 13, "grade": "A"}
]

sorted(students_dict, key=itemgetter("age"))

Output:

[{'age': 12, 'grade': 'A', 'name': 'John'},
 {'age': 13, 'grade': 'A', 'name': 'Ben'},
 {'age': 14, 'grade': 'B', 'name': 'Mary'}]

attrgetter()

Example:

class Student:
    def __init__(self, name, age, grade):
        self.name = name
        self.age = age
        self.grade = grade
    
    def __repr__(self):
        return repr((self.name, self.age, self.grade))

students = [
    Student("John", 12, "A"),
    Student("Mary", 14, "B"),
    Student("Ben", 13, "A")
]
[('John', 12, 'A'), ('Ben', 13, 'A'), ('Mary', 14, 'B')]

order

Both list.sort() and sorted() accept a reverse parameter with a boolean value. This is used to flag descending sorts" reverse=False and reverse=True represent ascending and descening order, respectively.

Advance

Multiple levels of sorting

Multiple level of sorting can be easily achieved with operator module functions.

For example, to sort by grade then by age:

>>> sorted(students_tuple, key=itemgetter(2, 1))
[('John', 12, 'A'), ('Ben', 13, 'A'), ('Mary', 14, 'B')]
>>> sorted(students, key=attrgetter("grade", "age"))
[('John', 12, 'A'), ('Ben', 13, 'A'), ('Mary', 14, 'B')]
>>> sorted(students_dict, key=itemgetter("grade", "age"))
[{'age': 12, 'grade': 'A', 'name': 'John'},
 {'age': 13, 'grade': 'A', 'name': 'Ben'},
 {'age': 14, 'grade': 'B', 'name': 'Mary'}]

Reference