Sometimes in Python, we need to sort a list of tuples based on multiple keys, each possibly with a different sorting order. For example, given a list like [('Alice', 25, 'C'), ('Bob', 22, 'A'), ('Alice', 30, 'B')]
, we may want to sort primarily by the first element, then by the second element in ascending order, and finally by the third element in descending order to receive [('Alice', 25, 'C'), ('Alice', 30, 'B'), ('Bob', 22, 'A')]
.
Method 1: Using the sorted() Function with a Custom Sorting Key
One of the most flexible approaches to sort a list of tuples is using the sorted()
function and specifying a custom sorting key via a lambda function that returns a tuple representing the multiple sorting keys. Python sorts based on the order of elements within the key tuple. This method provides clear sorting precedence, which is both customizable and readable.
Here’s an example:
tuples_list = [('Alice', 25, 'C'), ('Bob', 22, 'A'), ('Alice', 30, 'B')] sorted_list = sorted(tuples_list, key=lambda x: (x[0], x[1], -ord(x[2]))) print(sorted_list)
Output:
[('Alice', 25, 'C'), ('Alice', 30, 'B'), ('Bob', 22, 'A')]
This code snippet sorts the list of tuples first by the first element in each tuple, then by the second element, and finally in reverse order by the third element using the ASCII value (obtained via the ord()
function).
Method 2: The itemgetter Function from operator Module
The itemgetter
function from the operator
module creates a callable that serves as the key function for the sorted()
. It’s typically more efficient than a lambda and can simplify the code when sorting by multiple keys.
Here’s an example:
from operator import itemgetter tuples_list = [('Alice', 25, 'C'), ('Bob', 22, 'A'), ('Alice', 30, 'B')] sorted_list = sorted(tuples_list, key=itemgetter(0, 1, 2)) print(sorted_list)
Output:
[('Alice', 25, 'C'), ('Alice', 30, 'B'), ('Bob', 22, 'A')]
This code uses itemgetter()
to fetch the first, second, and third elements as a sorting key. Keep in mind that unlike lambda functions, using itemgetter()
doesn’t allow to easily change the sorting order for individual keys.
Method 3: In-Place Sorting Using the list.sort() Method
If you don’t need a new list and want to sort the tuples in place, you can use the list.sort()
method. It is slightly more efficient than sorted()
since it doesn’t need to create a new list.
Here’s an example:
tuples_list = [('Alice', 25, 'C'), ('Bob', 22, 'A'), ('Alice', 30, 'B')] tuples_list.sort(key=lambda x: (x[0], x[1], -ord(x[2]))) print(tuples_list)
Output:
[('Alice', 25, 'C'), ('Alice', 30, 'B'), ('Bob', 22, 'A')]
Here we have the same sorting logic as in the first method, but we are applying it directly to the list without creating a new sorted list.
Method 4: Sorting with Multiple Passes
If the sorting criteria are complex or you want to sort by one key and then by another while keeping the previous sort order, you can sort the list multiple times. This is less efficient than other methods but can be more straightforward to understand in some cases.
Here’s an example:
tuples_list = [('Alice', 25, 'C'), ('Bob', 22, 'A'), ('Alice', 30, 'B')] tuples_list.sort(key=lambda x: -ord(x[2])) # Sort by third element descending tuples_list.sort(key=lambda x: x[1]) # Stable sort by second element tuples_list.sort(key=lambda x: x[0]) # Stable sort by first element print(tuples_list)
Output:
[('Alice', 25, 'C'), ('Alice', 30, 'B'), ('Bob', 22, 'A')]
This code snippet employs a multi-pass sorting strategy, where each sort()
call is stable (it maintains the order of equal elements), ensuring that the final order reflects all sorting keys.
Bonus One-Liner Method 5: Using the attrgetter Function from operator Module
If the list consists of objects or namedtuples instead of plain tuples, attrgetter
can be used to sort by multiple attributes. Just like itemgetter
, it returns a callable that extracts the specified field(s).
Here’s an example:
from collections import namedtuple from operator import attrgetter Person = namedtuple('Person', 'name age grade') person_list = [Person('Alice', 25, 'C'), Person('Bob', 22, 'A'), Person('Alice', 30, 'B')] sorted_person_list = sorted(person_list, key=attrgetter('name', 'age', 'grade')) print(sorted_person_list)
Output:
[Person(name='Alice', age=25, grade='C'), Person(name='Alice', age=30, grade='B'), Person(name='Bob', age=22, grade='A')]
This code sorts a list of namedtuples based on multiple attributes using the attrgetter
and outputs a sorted version of the original list.
Summary/Discussion
- Method 1: Using
sorted()
with a lambda function. Strengths: Highly flexible and clear. Weaknesses: Lambda functions can be less efficient than itemgetter for large lists. - Method 2: Using
sorted()
withitemgetter
. Strengths: Efficient and concise for simple sorting criteria. Weaknesses: Does not support complex sorting logic where the order needs to be reversed for individual keys. - Method 3: Using
list.sort()
. Strengths: It sorts the list in place, which can be more efficient. Weaknesses: Modifies the original list, which might not always be desirable. - Method 4: Multiple passes with
sort()
. Strengths: Conceptually simple and very explicit. Weaknesses: Can be inefficient, especially for large datasets. - Bonus Method 5: Using
sorted()
withattrgetter
. Strengths: Ideal for sorting objects and namedtuples. Weaknesses: Only applicable to objects or namedtuples, not regular tuples.