Python List Sorting

Chapter 24 8 mins

Learning outcomes:

  1. Simple sorting using sort()
  2. Sorting in decreasing order using reverse
  3. Using the key parameter
  4. Sorting without mutating using sorted()

Introduction

Sorting is an extremely common task performed on lists in Python in many applications. A website needs to sort a list of users accordingly to their name; an online shopping application might want to sort products based on their prices; a university portal might want to sort students according to their GPA; and so on.

In addition to this, some common algorithms out there that operate on lists require them to be sorted. The highly efficient binary search is a perfect example.

In Python, there are two ways to sort a list: one is using the sort() method of lists, and the other is using the global sorted() function. In this chapterm we shall discover both these ways in meticulous detail.

Simple sorting

The most straightforward way to sort a list is to call sort() on it, without any arguments.

It will sort all the items of the list in increasing order, also known as ascending order, or alphabetical order.

The sort() method, as we already know, sorts a list by side effect. That is, it doesn't return a new sorted list; rather it sorts the list in place and returns None itself.

The sort() method mutates the list it's called on.

Following we have a list of numbers that we want to print in increasing order:

nums = [4, 0, 1, 18, 6, -3, -50]

To do so, we would go on and call the sort() method on this list and then print it:

nums = [4, 0, 1, 18, 6, -3, -50]

nums.sort()
print(nums)
[-50, -3, 0, 1, 4, 6, 18]

Calling nums.sort() sorts nums in place, after which we refer back to it in order to print it.

Sorting in decreasing order

To sort a list in decreasing order, set the reverse keyword argument to sort() to True.

list.sort(reverse=True)

As the name suggests, the reverse keyword argument specifies whether to sort the list in reverse order i.e decreasing order. By default, it's False and so the list doesn't get sorted in reverse order.

Below we sort the same list nums above, this time in decreasing order:

nums = [4, 0, 1, 18, 6, -3, -50]

nums.sort(reverse=True)
print(nums)
[18, 6, 4, 1, 0, -3, -50]

Sorting with a key

Apart from reverse, the sort() method can be provided one more keyword argument and that is key.

list.sort(key=None, reverse=False)

key specifies a function to be called on each element of the list being sorted to return a value to be used for comparison in the underlying sorting algorithm.

Let's see how it works.

Suppose we have a list of students, each represented using a tuple whose first item is the stundent's name and the second item is the student's age:

students = [("Mark", 20), ("James", 19), ("Sandra", 20), ("Bill", 21)]

Now let's say we want to sort this list based on the name of each student. Calling sort(), as is, won't work in this case since each item is a tuple, and therefore can't be validly compared with another one.

What we need to do is to tell Python what to use in comparing two items. This is where key comes in.

students = [("Mark", 22), ("James", 19), ("Sandra", 20), ("Bill", 21)]

def get_age(student):
    return student[1]

students.sort(key=get_age)
print(students)

We create a function get_age() and pass that as key to sort(). Now whenever an item in the list is compared against another (in the underlying sorting algorithm), this function is called on both of them and the names of the students retrieved, respectively.

Whichever student's age is smaller than the other one's, it gets ordered first.

[('James', 19), ('Mark', 20), ('Sandra', 20), ('Bill', 21)]

We can use key to sort lists in complex ways.

An example, sorting a list of co-ordinates based on their distances from the origin, is demonstrated below:

points = [(0, 5), (3, 5), (6, 6), (0, 0), (2, 9), (4, 12)]

def distance(point):
    # pythagoras's theorem
    return (point[0] ** 2 + point[1] ** 2) ** 2

points.sort(key=distance)
print(points)

Given a point (represented as a tuple of two items: the x co-ordinate followed by the y co-ordinate), the function distance() returns its distance from the origin, computed using the very famous Pythagoreon Theorem.

This function is set as the key of points.sort(). It's called by sort() for each of the elements of points, and then the comparison is made over the returned value, that is the distance of the respective point from the origin.

The point with the least amount of distance is put first, while the point with the largest distance is put last. In short, points is sorted according to the distance of each point, in increasing order.

Here's the output:

[(0, 0), (0, 5), (3, 5), (6, 6), (2, 9), (4, 12)]

Sorting using sorted()

Uptil this point in this chapter, we've been considering sorting examples using the sort() method of lists. The sort() method mutates a given list, and doesn't itself return anything.

This means that if we want to work with a sorted version of a list without modifying its original order, we have to do some additional work of copying the list.

In the code below, we ought to work with a sorted configuration of nums, but at the same time, need its original order preserved. This is possible by first making a copy of nums, and then sorting this copy:

nums = [8, 5, 1, 3, 0, 4, 4, 12, 19]

nums2 = nums[:]
nums2.sort()

# work with nums2
print(nums2)

The thing is that, often times, this boilerplate can become frustrating — we want to quickly work with a list's sorted version, and then forget it later on; why create another list?

This is where sorted() comes in.

sorted(key=None, reverse=False)

The sorted() function works exactly how sort() does — there is no single difference in the syntax of both these methods, except for that the former doesn't mutate the original list — rather it returns a new list in a sorted configuration.

The sorted() method is meant to right away return the sorted list, be it any order, using any key.

Here's a comparison of sort() and sorted():

Utilitysort() methodsorted() function
List mutationYesNo
Return valueNoneThe list
Parameterskey and reverseThe list to sort, key and reverse

Let's consider a quick example:

We want to print out each element in a list nums in increasing order, without modifying its original order. Obviously, this is much convenient using sorted():

nums = [8, 5, 1, 3, -19]

for num in sorted(nums):
    print(num)

print(nums)
-19
1
3
5
8
[8, 5, 1, 3, -19]

The first five prints here are the elements of nums in increasing order. Then the list nums is printed to see whether it has been changed or not. As expected, it is untouched.