Design a pure normalization pipeline for display labels
Also published in our Blogger archive.
A display-label normalizer is easier to reason about when each stage accepts one string and returns one new string. This example places the stages in a tuple and uses reduce() to pass the output of one stage to the next: Unicode NFC normalization, trimming, whitespace collapsing, and title casing. No stage reads global state, writes a file, or mutates a caller-owned object, so the function is pure for a given Python Unicode database and input string.
The concrete input contains leading and trailing spaces, repeated internal spaces, and the decomposed form e plus a combining acute accent. NFC combines that accent sequence where possible; the whitespace stages produce one separator; str.title() produces the display result Café Noir. The first assertion checks that the original string remains unchanged, while the second checks an idempotence property for this input: normalizing the already normalized label returns the same label.
Normalization is a display policy, not universal identity handling. NFC does not make all visually similar strings equal, and str.title() has language- and domain-specific limitations. For example, acronyms and apostrophe-containing names may need a product-specific rule. Keep the pipeline small and test representative labels before applying it to existing stored identifiers.
See the official unicodedata.normalize reference for normalization forms and functools.reduce for cumulative application.
AI assistance disclosure: this article was drafted with AI assistance and the label is synthetic.
from functools import reduce
from unicodedata import normalize
def collapse_spaces(text):
return " ".join(text.split())
def normalize_label(raw):
steps = (
lambda text: normalize("NFC", text),
str.strip,
collapse_spaces,
str.title,
)
return reduce(lambda value, step: step(value), steps, raw)
raw = " cafe\u0301 noir "
label = normalize_label(raw)
assert raw == " cafe\u0301 noir "
assert label == "Café Noir"
assert normalize_label(label) == label
print(label)
Café Noir