Two-part column headers were glued into single strings with an underscore, and a later step that split them back mis-assigned one column — how did that happen, and how do you prevent it?
answer
- reversible only under a condition nobody states
- one part contains the separator
- the breakage is per-column, not global
- counts all pass; the piece count does not
- guard where the parts still exist
basics
~20 sOne label part already contained an underscore, so that header split into more pieces than it was built from and the pieces landed in the wrong slots. Gluing is reversible only while no part can contain the separator, so the check belongs at glue time.
solid answer
~50 sGluing replaces a two-part label with one string built from its parts and a separator. It is reversible only under a condition nobody states: no part contains the separator. One label is enough to break it, and the damage is **per-column** — that column's string yields three or four pieces instead of two, so its measure and its region come out wrong while every other column is fine. Nothing structural changes, so a row count and a column count both still pass. The defence is to put the guard where the information still exists: refuse to glue when any part contains the chosen separator, and fail there. Better still, do not re-derive the parts at all — keep them as structure where the tool has it, or build a mapping from glued name to parts at the moment you glue and carry it beside the table.
code
pseudocode · 11 linesparts = ["net_revenue", "eu_west"] # the two label parts, taken from data values
separator = "_"
glued = glue(parts, separator) # "net_revenue_eu_west"
recovered = split(glued, separator) # ["net", "revenue", "eu", "west"] - four, not two
# measure becomes "net", region becomes "revenue": both wrong, for this column only
# the check belongs here, where the parts are still separate:
for part in parts:
if contains(part, separator):
fail("label part contains the separator: " + part)go deeper
Remember the condition: joining two label parts with a separator can only be undone if neither part contains that separator. Choose one that cannot appear inside a name.
Explain why the failure is per-column and silent: only the offending label splits into the wrong number of pieces, and the table's shape is identical before and after.
Show where the guard belongs. Refusing to glue when a part contains the separator catches it while the parts still exist; asserting the piece count catches what slipped through. Structural checks catch nothing here.
Decide as a standing rule what leaves your reshaping steps — compound labels, or names your code composed with a mapping back to the parts. Re-deriving parts by parsing a data-derived string is the option that ages worst.
## What gluing is, and what it trades A widening — turning one column's distinct values into new headers, filled from a second column — driven by two name columns produces labels with two parts. Where the tool stores labels as structured parts, that structure survives; where it stores them as flat strings, or where you want a table any consumer can read, the usual move is **gluing**: replacing the multi-part label with one string built from its parts and a separator. The trade is explicit if you say it out loud. You gain a label every consumer understands. You give up the parts — unless you can get them back, and getting them back means parsing. ## Why one label is enough Parsing a glued label is reversible **only while no part contains the separator**. That is a statement about data values, because the parts came from data values. When one part contains the separator, that one string produces more pieces than it was built from, and the pieces are assigned to the wrong roles: - a measure named with an underscore in it glued to a region gives four pieces, not two; - the first piece is taken as the measure, so the measure is a fragment; - the second piece is taken as the region, so the region is also a fragment; - the remaining pieces are surplus, and what happens to them is whatever the parsing code does with an unexpected count — often nothing, because nothing checks. The failure is **per-column**, not global. The other ninety-nine labels split correctly, so the output looks overwhelmingly right, and the one wrong column is usually discovered downstream as a group that should not exist or a measure that appears twice under different fragments. ## Why the usual checks do not see it This is what makes it a senior question rather than a trivia question: | check | does it fire? | why | |---|---|---| | row count before and after | no | gluing and splitting touch labels only | | column count before and after | no | the same number of columns exists throughout | | labels are unique | no | a glued label containing an extra separator is still a unique string | | absent-value counts | no | no cell changed | | piece count per label after splitting | **yes** | the only check whose value actually differs | Every structural assertion you would reach for by instinct passes. The one assertion that catches it is the one about the parse itself. ## Where the guard belongs Put the guard where the information still exists. At split time the parts are already gone and all you can do is notice that the count is wrong. At glue time you are holding both parts and can refuse: 1. **Refuse to glue** when any part contains the chosen separator, and fail there with the offending part in the message. 2. **Choose a separator that cannot occur in a label by construction**, and if you cannot guarantee that, normalise or escape the parts before gluing so that it becomes true. 3. **Assert the piece count** whenever you do split, so a collision that slipped through is loud rather than silent. 4. Treat a bounded split from one end as a **mitigation, not a guarantee** — it survives a collision in the other part and still breaks on a collision in the anchored one. ## The stronger repairs The guards above make the failure loud. These remove it: - **Keep the structure where the tool has it.** Dropping a part, reordering the parts or renaming one needs no parsing at all, so there is no separator and no collision. You pay for this in consumers that must understand compound labels. - **Carry the parts alongside.** Build a mapping from each glued name to its parts at the moment you glue, and hand it along with the table. Every later step reads the mapping instead of re-deriving the parts from the string. This keeps universally-consumable names *and* keeps the parts, at the cost of one more object to pass around. - **Widen later.** Keep the long layout — one row per measurement, with the measure's name in a column of its own — for as long as the work allows, and widen at the very end, where nobody needs to take the names apart again. ## The part people miss The separator guarantee is not static. The label parts are data values, so the set of labels changes with the input: a separator that never occurred in the sample arrives next month inside a new category name, and the pipeline that has worked for a year mis-assigns one column. That is why the guard has to run on every execution rather than once during development, and why "an underscore is fine, labels never have underscores" is a statement about a sample rather than about the data.
- The label parts come from data values. What does that do to your separator guarantee?It stops being yours to make. The parts are whatever the input contained, so a separator absent from today's sample can arrive next month inside a new category name. Either constrain the parts — normalise or escape them at glue time — or stop treating the glued string as the source of truth for the parts.
- Is there a repair that involves no parsing at all?Yes: keep the parts. Where labels are structured, dropping or reordering a part is a labels-only change with nothing to parse. Where they are not, build the mapping from glued name to parts at the moment you glue and carry it beside the table, so every later step reads the mapping rather than re-deriving the parts.
- What would you assert automatically to catch this class of bug?Two things. At glue time, that no part contains the chosen separator — refuse otherwise. At split time, that every label yields exactly the expected number of pieces. Row and column counts cannot help, because neither changes when a label mis-splits, and unique-label checks pass too.
saying these in an interview costs you the question
- Says an underscore is safe because labels never contain one
- Checks the row and column counts and concludes nothing broke
- Treats a bounded split from one end as a guarantee rather than a mitigation
- Adds the guard at split time, when the original parts are already gone
- Assumes a separator that was safe in the sample stays safe forever
- Thinks gluing loses nothing because both names are still visible in the string