Compare records with a locale-independent comparison key
Also published in our Blogger archive.
Compare records with a locale-independent comparison key
By Batu
Sorting display text directly can make ordering depend on the process locale when a program uses locale-aware comparison functions. A comparison key can make the chosen rule explicit instead. This recipe uses str.casefold() to create an aggressive Unicode case-insensitive key, then returns (name.casefold(), record_id). The integer record_id supplies a deterministic tie-breaker when names fold to the same value.
The records ("Beta", 2), ("alpha", 3), and ("ALPHA", 1) sort to IDs 1, 3, then 2. Both spellings of alpha share the first key component "alpha", so the second component orders IDs one and three. Python compares tuples lexicographically, and sorted() evaluates the key once per input item before sorting, which keeps the key function straightforward.
This is locale-independent because the code neither reads nor changes LC_COLLATE, and casefold() follows Unicode case-folding rules rather than the host's collation tables. It is not a culturally correct collation algorithm: accented characters, language-specific ordering, normalization-equivalent spellings, and user expectations may require an ICU-style collation service. casefold() is also for comparison, not for preserving display text; the original names remain in the records and are printed unchanged.
AI assistance was used to draft this article.
records = [
("Beta", 2),
("alpha", 3),
("ALPHA", 1),
]
def comparison_key(record: tuple[str, int]) -> tuple[str, int]:
name, record_id = record
return (name.casefold(), record_id)
ordered = sorted(records, key=comparison_key)
ordered_ids = [record_id for _, record_id in ordered]
assert ordered_ids == [1, 3, 2]
assert [comparison_key(record) for record in ordered][:2] == [
("alpha", 1),
("alpha", 3),
]
for name, record_id in ordered:
print(f"{record_id}:{name}")
Expected stdout:
1:ALPHA
3:alpha
2:Beta