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.

What you will see

9996

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.

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.

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.

← Sorting data that does not fit
Previous Lesson
Sorting data that does not fit