Today we’ll be talking about sorting lists. We’ll be using the sort method and the built-in sorted function.
If you prefer to watch the video version first, here it is:
Table of Contents
Sorting Lists with the sort Function
One thing to keep in mind right from the beginning. Unlike in older versions of Python, we only can sort lists with all the elements of the same type. It is now impossible to sort lists with heterogenous elements. This is what happens if we try:
>>> lst = ['hi', 5, 'no', 3.12]
>>> lst.sort()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'str'
But, still, we can sort lists with different types of numeric values, like ints and floats (but not complex numbers):
>>> lst = [2, 6.8, 3, 0.022, 3.4e-8]
>>> lst.sort()
>>> lst
[3.4e-08, 0.022, 2, 3, 6.8]
We can even use the boolean literals True and False. They are treated as 1 and 0 respectively:
>>> lst = [2, 6.8, 3, True, 0.022, False, 3.4e-8]
>>> lst.sort()
>>> lst
[False, 3.4e-08, 0.022, True, 2, 3, 6.8]
Here’s how a list of lists gets sorted. The elements are compared in the order they’re in:
>>> lst = [[3, 6], [3], [3, 4, 2], [3, 4]]
>>> lst.sort()
>>> lst
[[3], [3, 4], [3, 4, 2], [3, 6]]
How about a list of strings? Have a look at this:
>>> lst = ['1aA', '1Aa', 'A1a', 'Aa1', 'a1A', 'aA1']
>>> lst.sort()
>>> lst
['1Aa', '1aA', 'A1a', 'Aa1', 'a1A', 'aA1']
The characters are compared one by one. Capital letters come before small letters and if there are digits, they come before capital letters, so the order is: digits – capital letters – small letters.
Note that in strings the comparison occurs between the individual digits one by one, not the values of the numbers:
>>> lst = ['10', '11', '101']
>>> lst.sort()
>>> lst
['10', '101', '11']
Here’s another example:
>>> lst = ['can', 'CAR', 'cAt']
>>> lst.sort()
>>> lst
['CAR', 'cAt', 'can']
The key Argument
As capital letters come before small letters, ‘can’ is last instead of first. But we can pass a keyword argument called key and set it to str.casefold if we don’t want the case to matter:
>>> lst = ['can', 'CAR', 'cAt']
>>> lst.sort(key = str.casefold)
>>> lst
['can', 'CAR', 'cAt']
Alternatively you can set key to str.lower or str.upper so that the characters are converted to all lowercase or all uppercase respectively before they are compared. This conversion is used by the sort function, but it doesn’t permanently modify the strings in the list:
>>> lst = ['can', 'CAR', 'cAt']
>>> lst.sort(key = str.lower)
>>> lst
['can', 'CAR', 'cAt']
>>>
>>>
>>> lst = ['can', 'CAR', 'cAt']
>>> lst.sort(key = str.upper)
>>> lst
['can', 'CAR', 'cAt']
Lambda Expressions
You can also use the key with a lambda expression:
>>> lst = ['can', 'CAR', 'cAt']
>>> lst.sort(key = lambda x: x.lower())
>>> lst
['can', 'CAR', 'cAt']
Built-In Functions
You can assign a built-in function to key as well. Here’s how we can use the len function to sort the strings from shortest to longest:
>>> lst = ['from', 'to', 'between', 'for', 'opposite', 'under']
>>> lst.sort(key = len)
>>> lst
['to', 'for', 'from', 'under', 'between', 'opposite']
Custom Functions
And now a more complicated example. Suppose we want to sort the strings by the last character. We can define a function that returns the last character and assign it to the key argument:
>>> def last_character(s):
... return s[-1]
...
>>> lst = ['Greece', 'Brazil', 'Germany', 'Finland', 'Mexico', 'Canada', 'Peru']
>>> lst.sort(key = last_character)
>>> lst
['Canada', 'Finland', 'Greece', 'Brazil', 'Mexico', 'Peru', 'Germany']
Sorting in Descending Order
As you can see, it’s up to you how you sort the elements of your list. Up to now we’ve been using ascending order. If you want to sort your list in descending order, you should set the value of the reverse keyword argument to True:
>>> lst = [4, 2, 8, 3, 11, 5]
>>> lst.sort(reverse = True)
>>> lst
[11, 8, 5, 4, 3, 2]
Exercise
And now a short exercise:
We have a list of 3-tuples:
coordinates = [(3, 5, 7), (4, 2, 6), (7, 1, 9), (3, 3, 7)]
The three values in each tuple are the x, y and z coordinates in 3-dimensional space. We want to sort the coordinates by the y-coordinate, which is the middle one, at index 1, in descending order.
Solution
OK, we could define a function and assign it to the key argument, but instead we’ll use a lambda expression. Here’s the solution:
>>> coordinates = [(3, 5, 7), (4, 2, 6), (7, 1, 9), (3, 3, 7)]
>>> coordinates.sort(key = lambda tup: tup[1], reverse = True)
>>> coordinates
[(3, 5, 7), (3, 3, 7), (4, 2, 6), (7, 1, 9)]
Sorting Lists with the sorted Function
When we use the sort function, the list is changed in place. The sort function modifies the list, but it returns None, so you shouldn’t use it in an assignment like this:
>>> lst = [1, 4, 2, 8, 5]
>>> lst = lst.sort()
>>> type(lst)
<class 'NoneType'>
As you can see, all you have achieved by assigning the result of the sort method to the list is that you lost the reference to the list. Instead, the list is now of type None.
But it’s also possible to sort a list without changing it in place. To this end we can use the built-in sorted function and pass the list as an argument to it. Let’s rewrite the previous example using the sorted function:
>>> lst = [1, 4, 2, 8, 5]
>>> sorted(lst)
[1, 2, 4, 5, 8]
>>> lst
[1, 4, 2, 8, 5]
This time the original list doesn’t change. You can assign the result of the sorted method to a variable and so you can save the sorted list without modifying the original one:
>>> nums = [3, 2, 8, 1, 9, 5]
>>> nums_asc = sorted(nums)
>>> nums_asc
[1, 2, 3, 5, 8, 9]
>>> nums_desc = sorted(nums, reverse = True)
>>> nums_desc
[9, 8, 5, 3, 2, 1]
>>> nums
[3, 2, 8, 1, 9, 5]
In the sorted function you can also set the key argument to a function and so determine how the sorting should be performed. Here’s an example. A slightly tougher one!
We have a list of friends:
>>> friends = ["old Jim Brown",
... "pretty but not too smart Jane Smith",
... "silly Mike Lee",
... "my little Sarah Dubois",
... "amazing Frankie Jackson"]
The list contains descriptions of our friends. The last but one word in each description is the first name. Using the sorted function we want to sort the list by first names. Here’s how we can do it with a method defined beforehand:
>>> def first_name(description):
... return description.split()[-2]
...
>>> sorted(friends, key = first_name)
['amazing Frankie Jackson', 'pretty but not too smart Jane Smith', 'old Jim Brown', 'silly Mike Lee', 'my little Sarah Dubois']
And the same with a lambda:
>>> sorted(friends, key = lambda n: n.split()[-2])
['amazing Frankie Jackson', 'pretty but not too smart Jane Smith', 'old Jim Brown', 'silly Mike Lee', 'my little Sarah Dubois']