What a key does
By default, sorted puts numbers from small to large and text from A to Z. Often you need another order: the highest price first, the shortest word first, or names ordered by their last letter. For that, give sorted a key.
The key is a small function. sorted calls it once on every item, then orders the items by what the key returned. The items themselves are not changed, and the list you get back still holds the original items.
A lambda is a function written in one line, with no name. lambda w: len(w) takes one value, w, and returns len(w). It does the same as a def function that holds only a return line.
words = ["pear", "fig", "banana", "kiwi"]
print(sorted(words, key=lambda w: len(w)))
print(sorted(words, key=len))
['fig', 'pear', 'kiwi', 'banana']
['fig', 'pear', 'kiwi', 'banana']
Both lines give the same list. When an existing function such as len already does the job, you can pass it directly.
High to low
For numbers there are two ways. The first negates the key: the biggest number has the smallest -n, so it comes first. The second is the reverse=True option.
scores = [70, 95, 82]
print(sorted(scores, key=lambda s: -s))
print(sorted(scores, reverse=True))
[95, 82, 70]
[95, 82, 70]
Negating only works for numbers. You can't write -name for a string, so for text use reverse=True.
Two rules at once
When one rule isn't enough, let the key return a tuple. Python compares tuples item by item: it looks at the first items, and only when they are equal does it look at the second ones.
nums = [25, 13, 5, 41, 33]
print(sorted(nums, key=lambda n: (n % 10, n)))
print(sorted(nums, key=lambda n: n % 10))
[41, 13, 33, 5, 25]
[41, 13, 33, 25, 5]
The key (n % 10, n) gives each number its last digit first, then the number itself. So 41 comes first (last digit 1), then 13 and 33 (last digit 3, small to large), and 5 and 25 close the list (last digit 5, small to large).
The second line uses only the last digit. Items with equal keys keep the order they had in the original list, because the sort is stable. That is why 25 stays before 5 there. If you want a particular order for ties, put it in the tuple as a second rule.
sorted and sort
sorted(items) makes a new list and leaves the old one as it was. The list method lst.sort(key=...) sorts the list itself and returns None.
lst = [3, 1, 2]
result = lst.sort(key=lambda n: -n)
print(result)
print(lst)
None
[3, 2, 1]
That is why lst = lst.sort() is a bug: it replaces your list with None.
The lesson's task
A pet shop has prices and names. You need highFirst, the prices from high to low, and byLastLetter, the names ordered by their last letter, with names that share a last letter going from A to Z. The loops that print both are already in the starter code.
The first list needs one rule: the key turns each price into -price. The second needs two rules, so the key returns a tuple. The last letter of a name is name[-1], because a negative index counts from the end. The second item of the tuple is the whole name.
Here are the keys the second list uses:
names = ["Rex", "Luna", "Bella", "Max", "Bo", "Coco"]
print([(name[-1], name) for name in names])
[('x', 'Rex'), ('a', 'Luna'), ('a', 'Bella'), ('x', 'Max'), ('o', 'Bo'), ('o', 'Coco')]
Sorted by these tuples, the a names come first (Bella before Luna), then o (Bo before Coco), then x (Max before Rex). Here is the whole solution:
prices = [12, 5, 30, 8]
names = ["Rex", "Luna", "Bella", "Max", "Bo", "Coco"]
highFirst = sorted(prices, key=lambda price: -price)
for price in highFirst:
print(price)
byLastLetter = sorted(names, key=lambda name: (name[-1], name))
for name in byLastLetter:
print(name)
It prints:
30
12
8
5
Bella
Luna
Bo
Coco
Max
Rex
Common mistakes
- Forgetting the second rule. With
key=lambda name: name[-1]alone, ties keep their original order, and the result is['Luna', 'Bella', 'Bo', 'Coco', 'Rex', 'Max']. Rex comes before Max only because it was first in the list. - Calling the key instead of passing it. Write
key=len, notkey=len(). - Using
reverse=Truewith the tuple key. It flips both rules, so the names come out as['Rex', 'Max', 'Coco', 'Bo', 'Luna', 'Bella'], with Z to A inside each group. - Assigning the result of
sort(). It returnsNone.
Key takeaways
- Give
sortedakeyfunction to choose what each item is sorted by. - Return a tuple from the key to apply several rules in order.
- Use
-norreverse=Trueto sort numbers from high to low. sortedreturns a new list;sortchanges the list and returnsNone.
