Master
A comparator of your own
Sometimes the order is not a tuple. Then you write a comparison that says which item comes first, and make sure it is a consistent order.
About 2 min of reading
Quick check
Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.
chunks = [sorted(nums[i:i + chunk]) for i in range(0, len(nums), chunk)]
print(chunks)
chunk = 2
nums = [9, 1, 7, 3, 8, 2]
Check order
Reset
Try It
Order items by a custom rule.
1
Sort by a rank map.
order = {"low": 0, "mid": 1, "high": 2}
rows = ["high", "low", "mid"]
print(sorted(rows, key=order.get))
Before you run it, say out loud what it will print. Then open the answer.
What it prints
['low', 'mid', 'high']
2
Sort with a comparator.
from functools import cmp_to_key
def cmp(a, b):
return -1 if a + b > b + a else 1
nums = [3, 30, 34]
print(sorted(nums, key=cmp_to_key(cmp)))
Before you run it, say out loud what it will print. Then open the answer.
What it prints
[34, 3, 30]
3
Reverse one field only.
rows = [(1, 3), (1, 1)]
rows.sort(key=lambda r: (r[0], -r[1]))
print(rows)
Before you run it, say out loud what it will print. Then open the answer.
What it prints
[(1, 3), (1, 1)]
4
Compare comparator calls.
import math
print(round(1000 * math.log2(1000)))
Before you run it, say out loud what it will print. Then open the answer.
What it prints
9996
Put it in order
Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.
print(sorted(rows, key=order.get))
rows = ["high", "low", "mid"]
order = {"low": 0, "mid": 1, "high": 2}
Check order
Reset
Data Flow
Custom ordering uses a comparator or a rank map, and the order must be a consistent total order for the sort result to be reliable.
1
custom rule
↓
2
rank map or comparator
↓
3
must be a total order
↓
4
key cheaper than comparator
Line by line
Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.
order = {"low": 0, "mid": 1, "high": 2}
sets nothing on screen
rows = ["high", "low", "mid"]
sets nothing on screen
print(sorted(rows, key=order.get))
['low', 'mid', 'high']
Why this works
Key beats comparator
Express the order as a value when you can. The key runs once per item; a comparator runs once per comparison, about n log n times.
A bad comparator breaks sorts
If the comparison is not consistent, the sorted result is not predictable. Test the three rules: reflexive, antisymmetric, transitive.
Examples
cmp_to_key
from functools import cmp_to_key
def cmp (a, b):
return -1 if a + b > b + a else 1
nums = [3 , 30 , 34 ]
print (sorted (nums, key=cmp_to_key (cmp)))
The comparator decides order by rule. Here it puts the concatenation that reads bigger first.
Consistency is required
rows = [1 , 2 , 3 ]
print (sorted (rows, key=lambda x: x))
A comparator must agree with itself. If a beats b and b beats c, a must beat c, or the sort can misbehave.
Total order test
rows = [(1 , 2 ), (1 , 2 )]
print (sorted (rows))
Equal items must compare equal, not one before the other. A partial order leaves some pairs unordered.
Sort by a custom rank
order = {"low" : 0 , "mid" : 1 , "high" : 2 }
rows = ["high" , "low" , "mid" ]
print (sorted (rows, key=order.get))
A dictionary maps each label to its rank. Sorting by the rank gives the order you chose.
Tie-break with an index
rows = [(5 , "a" ), (5 , "b" ), (5 , "c" )]
rows.sort (key=lambda r: r[0 ])
print (rows)
Stable sort keeps input order on ties. If that is not the order you want, add the tiebreaker field.
Reverse one field only
rows = [(1 , 3 ), (1 , 1 )]
rows.sort (key=lambda r: (r[0 ], -r[1 ]))
print (rows)
Negate the field that should descend. The other fields stay ascending.
Case-insensitive order
words = ["Owlww" , "apple" , "Bee" ]
print (sorted (words, key=str.lower))
Lower-casing in the key gives dictionary order. The default puts all capitals before all lowercase.
Comparator cost
import math
print (round (1000 * math.log2 (1000 )))
A comparator runs about n log n times. A key function runs n times, so a key is cheaper when it can express the order.
Words this lesson uses
Each one is defined here on this site. Open the entry for the full definition and the other words it points at.