skip to content

Working a Column of Text

Cleaning text is most of the real work and the place the whole-column habit breaks: the surface reads like one pass over the column while underneath many of these are still a call per value.

on this pageshow

questions

4

A column of 200,000 customer names is folded to one case and trimmed, yet a later check still sees the old values — why?

level: juniorimportance: must knowfreq 70%

answer

  1. one statement, many results
  2. where did the result go?
  3. a returned column, not an edit
  4. assign it back under a name
  5. no error means nothing went wrong

basics

~20 s

The ordinary whole-column text operation computes a new column and returns it; it does not edit the values where they sit. If nothing captures the result, or it is captured under a name the later check does not read, the table keeps the old values and no error is raised.

solid answer

~50 s

Saying a text job once for the whole column — fold to one case, trim the whitespace around each value — is an expression, not an edit. It reads the column, produces a new column of the same length, and hands it back. In most designs a single text value is immutable anyway, so a changed value is necessarily a new value. Three shapes of result travel around this family: a returned column you must assign onto the table, a verb returning a whole new table with that column replaced, and, less commonly, an in-place form that does mutate. The first two are the usual case and both require you to take the result. The other way this symptom appears is that the cleaned values were captured under `names_clean` while the grouping downstream still reads `names`.

go deeper

for a junior

Recall that these operations return a result. Say the job once for the whole column, then assign what comes back onto the table under a name, and check a few actual values afterwards.

for a middle

Explain why the operation returns rather than mutates: the values are typically immutable, the tool cannot pick your output name, and an expression that leaves its inputs alone composes safely in a chain.

for a senior

Demonstrate the discipline that prevents this class outright: normalise once, near the read, assert that no value differs from its cleaned form, and make every downstream reader use the cleaned name.

for a principal

The tradeoff worth owning is where normalisation lives. Cleaning at the boundary once is cheap and provable; cleaning at each point of use is repeated work and drifts, because each site normalises slightly differently.

## The unit of work is the column, not the record A text column here is 200,000 values you want treated identically: every value folded to one case, every value stripped of the whitespace surrounding it. Every tool in this family lets you say that **once for the whole column** rather than writing a loop over records in your own program. Call that grouped set of text jobs exposed on a column **the text surface on a column**: it reads like one pass over the column, whether or not it is one underneath. How fast it runs is a separate question and depends on how the values are stored. What matters here is simpler and catches people earlier: **what the operation hands back**. ## It computes a result; it does not change the values where they sit In the ordinary form the operation is an expression. It reads the column, produces a new column of the same length, and returns it. It writes nothing into the table you read from. Two things reinforce that: - In most of these designs a single text value is **immutable**. There is no "change it where it sits" available at all, so a changed value is necessarily a newly built value. - The tool cannot know what name you want the cleaned values under. Overwriting the source silently would destroy the column you may still need to compare against. So the statement runs, 200,000 clean values are produced, and if nothing captures them the entire result is discarded when the statement ends. The table is exactly as it was, and nothing is raised, because nothing went wrong. ## Three shapes the result comes back in | How the tool hands the work back | What you must do | What forgetting looks like | |---|---|---| | A new column returned from the expression | Assign it onto the table under a name — the same name to overwrite, a new one to keep both | The table keeps the old values | | A verb that returns a whole new table with that column replaced | Bind the returned table, or keep the chain going from it | You carry on working with the table you started from | | An in-place form, where the tool offers one | Nothing further | The change is visible through anything else pointing at that column, which is not always what you wanted | The first two are the common cases across this family and both require you to take the result. The third genuinely mutates and is the exception, not the rule. ## Why case and whitespace in particular These two jobs bite harder than any other text clean-up because **their effect is invisible when you look**. A trailing space does not render. A difference between one casing and another vanishes in a printed sample the eye skims. So the code that was never applied looks identical to the code that was, right up to the point where two values that should be the same value fail to be treated as the same value. Numeric clean-up announces itself; text clean-up does not. ## The other ways "nothing changed" happens - **Captured under the wrong name.** The result is assigned, but to a new name, while the check, the grouping or the match downstream still reads the original. The fix here is a stale reader, not a missing assignment — different bug, same symptom. - **Applied to a selection rather than the column.** The clean-up runs over a subset of rows, the result is shorter than the table, and assigning it back lines up against only part of it. Compare the length of what you assign against the row count before assigning. - **Applied and then overwritten.** A later step rebuilds the column from the original source and the clean-up is undone. Doing the clean-up once, early, near where the data is read, makes this hard to do by accident. ## How to check that it actually happened 1. Assert it rather than eyeballing it: count the values that still differ from their own cleaned form. Zero is what you want, and it is one line. 2. Look at individual values, not at a summary of the column. A row count is unchanged either way, and so is most of what a summary reports. 3. Normalise once, early, and keep the cleaned column as the one everything downstream reads. ## What this does not tell you Writing the job as one whole-column statement tells you that **you** are not looping. It tells you nothing about whether the tool is looping underneath — a column-shaped text surface can still be one call per value depending on how the text is stored, and the two run at very different speeds. The surface shape and the execution are two separate claims, and this one is only about the first.

  • The cleaned column was assigned back correctly, and a later match still fails on some rows. What is left to look at?
    Whether the clean-up you applied covers the difference that is actually there. Folding case and trimming the outer whitespace do nothing to whitespace inside a value, to a different separator, or to a prefix one side carries. Compare the two failing values character by character rather than by eye, and widen the normalisation to whatever the comparison shows.
  • Why do these tools return a new column rather than changing the one you gave them?
    Because the expression cannot know which name you want the result under, because in most of these designs a single text value is immutable so a change is necessarily a new value, and because an expression that leaves its inputs alone is safe to reuse in a chain. The in-place forms that some tools offer trade that safety for not allocating a second column.
  • Is one whole-column clean-up statement faster than a loop you write over the records?
    Usually, but not because it is column-shaped. The gain is real when the tool can run the job as one compiled pass over the values, and it largely disappears when the values are stored as separate language objects and the surface ends up calling out once per value. The surface shape is not a promise about execution.

saying these in an interview costs you the question

  • Says a whole-column text operation edits the values where they sit
  • Takes the absence of an error as proof the clean-up applied
  • Claims you must loop over records to change every value
  • Treats a returned table as if the original had been changed
  • Believes an unchanged row count proves the clean-up landed
  • Assumes case and whitespace differences would have been visible in a sample
open as a page

Arithmetic over a 5,000,000-row column takes milliseconds while the same-shaped whole-column text normalisation takes half a minute — why?

level: seniorimportance: must knowfreq 64%

basics

~20 s

A column-shaped text surface is a way of writing the job, not a promise about how it runs. Where text is held as packed bytes with an offset per row, compiled code walks it once; where each value is a separate language object, the same-looking call is one call per value — the loop you thought you had removed.

open as a page

When a column of address lines is split on a separator into several columns, what fixes how many columns come back?

level: middleimportance: should knowfreq 52%

basics

~20 s

Nothing in a single row can fix it: the result is a rectangle, so one width must cover every row. Designs resolve that by scanning the column and taking the widest row, by making you declare the width or the output names, or by refusing ragged input outright.

open as a page

A pattern extraction over a 2,000,000-row text column raises nothing, yet 340,000 rows come back with no value — what do you check?

level: seniorimportance: should knowfreq 58%

basics

~20 s

Extraction over a column is total, not validating: a row the pattern does not fit yields no value rather than an error, so misses are silent. Check how many of those rows held no value beforehand, sample the rest, and put a bound on the miss rate so the next run fails loudly.

open as a page