Custom Sort Keys — Rules from the Problem, Not the Library
Interviewers rarely ask only “sort ascending.” They ask: sort rows by column 2 descending, then column 1 ascending as a tie-breaker. That is custom sorting—turning business rules into a key function or tuple ordering that the sort algorithm consumes.
Why this shows up in the real world
A ride‑hailing app might list drivers by ETA, then rating, then distance as a deterministic tie-break so two users never argue about arbitrary ordering. Sports leagues publish standings with primary points, then goal difference, then goals scored—exactly a lexicographic tuple sort on rows. Bug trackers sort issues by severity, then age, then reporter ID to keep triage fair. Python’s sorted(..., key=...) is the ergonomic face of these policies; databases expose the same idea as ORDER BY clauses.
Core idea (explained for students)
Reduce your objects to a sortable key: a tuple of primitives where Python’s lexicographic ordering matches your policy. For descending on a numeric field, negate the field (if all numbers) or sort by (-field, secondary). When keys are expensive to compute, use Schwartzian transform (decorate‑sort‑undecorate): precompute keys once. Always document whether stability matters—Python’s sorted and list.sort are stable, so equal primary keys preserve original order for free tie‑breaks.
Try this in Python
rows = [
("ana", 92, 1),
("bob", 92, 2),
("cara", 88, 3),
]
# Sort by score descending, then name ascending
print(sorted(rows, key=lambda r: (-r[1], r[0])))
Common mistakes
- Mixing ascending/descending fields without a clear tuple plan—tests become flaky.
- Negating floats for descending—NaN and -0 surprises.
- Sorting mutable lists that are shared aliases—side effects bite.
Key takeaways
- Encode policy as tuple keys or explicit
key=lambdas. - Exploit stable sorting for multi-level tie logic.
- Prefer
operator.attrgetter/itemgetterfor speed and readability on large tables.
