Python Basics — sorted() with Key
sorted() with Key
Python’s sorted() function is incredibly flexible. The key parameter lets you define custom sort orders using any function — including lambdas.
sorted() Basics
numbers = [5, 2, 8, 1, 9, 3]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [1, 2, 3, 5, 8, 9]
print(numbers) # [5, 2, 8, 1, 9, 3] — original unchanged
# Reverse order
desc = sorted(numbers, reverse=True)
print(desc) # [9, 8, 5, 3, 2, 1]
sorted()returns a new list. The original is unchanged. To sort in-place, uselist.sort().
The key Parameter
The key function is called on each element before comparison. It determines what value to sort by:
words = ["banana", "apple", "cherry", "date"]
# Sort by string length
by_length = sorted(words, key=len)
print(by_length) # ['date', 'apple', 'banana', 'cherry']
# Sort alphabetically (case-insensitive)
mixed = ["Banana", "apple", "Cherry", "date"]
by_lower = sorted(mixed, key=str.lower)
print(by_lower) # ['apple', 'Banana', 'Cherry', 'date']
Key with Lambda
# Sort by last character
words = ["hello", "world", "python", "java"]
by_last = sorted(words, key=lambda w: w[-1])
print(by_last) # ['java', 'world', 'python', 'hello']
# Sort by absolute value
numbers = [-5, 3, -1, 8, -2, 7]
by_abs = sorted(numbers, key=lambda x: abs(x))
print(by_abs) # [-1, -2, 3, -5, 7, 8]
# Shorter: use abs directly
by_abs = sorted(numbers, key=abs)
print(by_abs) # [-1, -2, 3, -5, 7, 8]
Sorting Complex Objects
Lists of Tuples
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78), ("Diana", 92)]
# Sort by score
by_score = sorted(students, key=lambda s: s[1])
print(by_score)
# [('Charlie', 78), ('Alice', 85), ('Bob', 92), ('Diana', 92)]
# Sort by score descending
by_score_desc = sorted(students, key=lambda s: s[1], reverse=True)
print(by_score_desc)
# [('Bob', 92), ('Diana', 92), ('Alice', 85), ('Charlie', 78)]
Lists of Dictionaries
employees = [
{"name": "Alice", "salary": 75000, "department": "Engineering"},
{"name": "Bob", "salary": 65000, "department": "Marketing"},
{"name": "Charlie", "salary": 85000, "department": "Engineering"},
{"name": "Diana", "salary": 70000, "department": "Marketing"},
]
# Sort by salary
by_salary = sorted(employees, key=lambda e: e["salary"])
for emp in by_salary:
print(f"{emp['name']}: ₹{emp['salary']}")
# Bob: ₹65000
# Diana: ₹70000
# Alice: ₹75000
# Charlie: ₹85000
# Sort by department, then by salary within department
by_dept_salary = sorted(employees, key=lambda e: (e["department"], e["salary"]))
for emp in by_dept_salary:
print(f"{emp['department']} — {emp['name']}: ₹{emp['salary']}")
# Engineering — Alice: ₹75000
# Engineering — Charlie: ₹85000
# Marketing — Bob: ₹65000
# Marketing — Diana: ₹70000
Multi-Key Sorting with Tuples
Return a tuple from the key function to sort by multiple criteria:
data = [
("Alice", "B", 85),
("Bob", "A", 92),
("Charlie", "B", 78),
("Diana", "A", 95),
]
# Sort by grade (ascending), then by score (descending)
# Negate the score to reverse just that field
result = sorted(data, key=lambda x: (x[1], -x[2]))
for name, grade, score in result:
print(f"{grade}: {name} ({score})")
# A: Diana (95)
# A: Bob (92)
# B: Alice (85)
# B: Charlie (78)
operator.itemgetter and operator.attrgetter
The operator module provides efficient key functions:
from operator import itemgetter
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
# Same as key=lambda s: s[1]
by_score = sorted(students, key=itemgetter(1))
print(by_score) # [('Charlie', 78), ('Alice', 85), ('Bob', 92)]
# Multi-key with itemgetter
data = [("Alice", "B", 85), ("Bob", "A", 92), ("Charlie", "B", 78)]
by_grade_name = sorted(data, key=itemgetter(1, 0))
print(by_grade_name)
# [('Bob', 'A', 92), ('Alice', 'B', 85), ('Charlie', 'B', 78)]
from operator import attrgetter
class Student:
def __init__(self, name, gpa):
self.name = name
self.gpa = gpa
def __repr__(self):
return f"Student({self.name}, {self.gpa})"
students = [Student("Alice", 3.8), Student("Bob", 3.2), Student("Charlie", 3.9)]
by_gpa = sorted(students, key=attrgetter("gpa"), reverse=True)
print(by_gpa) # [Student(Charlie, 3.9), Student(Alice, 3.8), Student(Bob, 3.2)]
Stable Sort Behavior
Python’s sort is stable — elements that compare equal maintain their original order:
data = [
("Alice", "Engineering"),
("Bob", "Marketing"),
("Charlie", "Engineering"),
("Diana", "Marketing"),
]
# Sort by department — Alice stays before Charlie, Bob stays before Diana
by_dept = sorted(data, key=lambda x: x[1])
print(by_dept)
# [('Alice', 'Engineering'), ('Charlie', 'Engineering'), ('Bob', 'Marketing'), ('Diana', 'Marketing')]
This makes multi-pass sorting safe:
# Sort by name first, then by department (stable sort preserves name order within departments)
result = sorted(data, key=lambda x: x[0]) # first by name
result = sorted(result, key=lambda x: x[1]) # then by dept
print(result)
# [('Alice', 'Engineering'), ('Charlie', 'Engineering'), ('Bob', 'Marketing'), ('Diana', 'Marketing')]
sorted() vs list.sort()
| Feature | sorted() |
list.sort() |
|---|---|---|
| Returns | New sorted list | None (sorts in place) |
| Works on | Any iterable | Only lists |
| Original | Unchanged | Modified |
| Memory | Creates new list | No extra memory |
numbers = [5, 2, 8, 1]
# sorted() — returns new list
new_list = sorted(numbers)
print(numbers) # [5, 2, 8, 1] — unchanged
print(new_list) # [1, 2, 5, 8]
# list.sort() — modifies in place
numbers.sort()
print(numbers) # [1, 2, 5, 8] — changed
Summary
sorted(iterable, key=func, reverse=bool)returns a new sorted list- The
keyfunction determines what to sort by — called on each element - Use lambda for simple key functions, named functions for complex ones
- Sort by multiple criteria using tuple keys:
key=lambda x: (x[0], -x[1]) operator.itemgetterandattrgetterare efficient alternatives to lambdas- Python’s sort is stable — equal elements keep their original order