| name | spreadsheet-data-cleaning |
| description | Produces spreadsheet-native data cleaning formulas and operations. Generates exact TRIM, CLEAN, TEXT, SUBSTITUTE formulas for text issues, IF/IFERROR wrappers for null handling, COUNTIF for duplicate detection, and a cleaning sequence with before/after column strategy.
Use when the user needs to clean data using spreadsheet formulas rather than external tools.
Do NOT use for a full data cleaning protocol across any tool (use data-cleaning-protocol), setting up validation rules (use data-validation-setup), or auditing a spreadsheet model (use spreadsheet-model-audit).
|
| license | Apache-2.0 |
| metadata | {"author":"foundry-skills","version":"1.0.0","tags":"spreadsheets data-science template","category":"data-analysis","subcategory":"spreadsheets","depends":"","disclaimer":"none","difficulty":"intermediate"} |
Spreadsheet Data Cleaning
When to Use
Use this skill when:
- The user has a dataset open in Excel or Google Sheets and describes specific data quality problems -- extra spaces, inconsistent casing, phone numbers in multiple formats, dates stored as text, currency fields with symbols embedded, or columns with mixed types
- The user asks a formula-level question such as "how do I strip the dollar sign from this column," "how do I find all the duplicate emails," or "how do I fill in the blanks with the value from the row above"
- The user needs a repeatable, auditable cleaning workflow that lives inside the spreadsheet itself -- no Python, no Power Query, no SQL -- just formulas dragged down a column
- The user is preparing data for a pivot table, VLOOKUP/XLOOKUP, dashboard, or chart and the joins or aggregations are failing because of formatting mismatches
- The user has received a data export (from a CRM, ERP, survey tool, or database) and needs to normalize it before analysis
- The user needs a non-destructive cleaning approach where the original data is preserved and cleaned versions live in helper columns for verification
- The user is working with a shared workbook and cannot run external scripts -- all transformations must be formula-based and reviewable by non-technical stakeholders
Do NOT use when:
- The user wants a tool-agnostic data cleaning protocol covering Python pandas, SQL, R, and spreadsheets -- use
data-cleaning-protocol instead, which handles cross-tool strategy
- The user wants to set up data validation rules (dropdown lists, input masks, range checks) to prevent dirty data from entering the spreadsheet in the future -- use
data-validation-setup
- The user wants to audit formula integrity, circular references, or dependency chains in a financial or analytical model -- use
spreadsheet-model-audit
- The user wants to write a Python script using pandas, OpenRefine, or a SQL-based cleaning pipeline -- this is outside the scope of spreadsheet-native formulas
- The user is working with datasets larger than ~500,000 rows in Excel -- at that scale, spreadsheet formulas become unreliable and Power Query or Python is more appropriate; redirect to
data-cleaning-protocol
- The user wants to set up macros or VBA to automate cleaning -- this skill is strictly formula-based and spreadsheet-portable
- The user is asking about data modeling, schema normalization, or database design -- those are structural concerns, not formula-level cleaning concerns
Process
Step 1: Diagnose the Data Quality Issues Before Writing a Single Formula
Before prescribing formulas, gather enough information to understand the shape of the problem. Ask the user for:
- Column inventory: What are the column names, data types (text, number, date, boolean), and approximate row count? A column described as "customer ID" behaves differently from "product description."
- Sample values: Request 3-5 representative dirty values per problem column. Do not assume what "inconsistent formatting" means -- "555-1234," "(555) 123-4567," and "5551234" are three different phone problems requiring different formulas.
- Source system: Was this exported from Salesforce, a MySQL dump, a survey platform, or typed manually? Source systems have predictable error patterns (e.g., Salesforce exports often have trailing spaces in lookup fields; Google Forms responses often have inconsistent casing).
- Cleaning intent: Does the user need exact matching (joining on customer ID), display formatting (printing a report), or analysis-readiness (summing a revenue column)?
- Destructive vs. non-destructive: Should cleaning happen in-place (overwriting original values after paste-as-values) or in new columns that run in parallel? Default to non-destructive unless the user explicitly says otherwise.
- Platform: Excel (which version -- 365, 2019, 2016?) or Google Sheets? XLOOKUP, LET, and BYROW are available in Excel 365 and Google Sheets but not in Excel 2016. ARRAYFORMULA is Google Sheets-only.
Run a quick audit with these diagnostic formulas before building the cleaning sequence:
=COUNTBLANK(A2:A1001) -- count empty cells in a 1000-row column
=SUMPRODUCT(--ISERROR(A2:A1001)) -- count error values (any type)
=SUMPRODUCT(--(LEN(A2:A1001)<>LEN(TRIM(A2:A1001)))) -- count cells with extra spaces
=SUMPRODUCT(--(A2:A1001<>LOWER(A2:A1001))) -- count cells that are not all lowercase
=SUMPRODUCT(--(ISNUMBER(A2:A1001)*1)) -- count numeric values in a column
These five formulas identify the scope of problems quantitatively before any cleaning begins. Report these counts to the user so they can verify the cleaning worked afterward.
Step 2: Apply Text Cleaning Formulas -- Strip, Normalize, Standardize
Text columns are the most common source of data quality failures. Apply formulas in the order below -- the sequence matters because CLEAN must run before TRIM (CLEAN can create spaces, TRIM removes them), and case normalization should run before SUBSTITUTE (so you match on a known case).
Whitespace and non-printable character removal:
| Problem | Formula | Notes |
|---|
| Leading/trailing/double spaces | =TRIM(A2) | Reduces all internal runs of spaces to a single space |
| Non-printable ASCII chars 0-31 | =CLEAN(A2) | Removes line feeds (CHAR 10), carriage returns (CHAR 13), tabs (CHAR 9) |
| Both problems together | =TRIM(CLEAN(A2)) | Standard baseline -- apply this to every text column by default |
| Non-breaking space (CHAR 160) | =TRIM(CLEAN(SUBSTITUTE(A2,CHAR(160)," "))) | CHAR(160) is invisible and survives TRIM without this SUBSTITUTE |
| Smart quotes (left/right) | =SUBSTITUTE(SUBSTITUTE(A2,CHAR(8220),""""),CHAR(8221),"""") | Replaces curved left (8220) and right (8221) double quotes with straight quotes |
| Em dash to hyphen | =SUBSTITUTE(A2,CHAR(8212),"-") | CHAR(8212) is the em dash; also handle en dash CHAR(8211) |
| Bullet points and symbols | =SUBSTITUTE(A2,CHAR(8226),"") | CHAR(8226) is the bullet; chain multiple CHAR SUBSTITUTEs as needed |
Case standardization:
| Output needed | Formula | Caution |
|---|
| All uppercase | =UPPER(A2) | Use for codes, IDs, country codes, state abbreviations |
| All lowercase | =LOWER(A2) | Use for email addresses, usernames, URLs |
| Title case | =PROPER(A2) | Use for names, addresses -- but PROPER incorrectly capitalizes "Of", "The", "McDonald" etc. |
| Sentence case (first letter only) | =UPPER(LEFT(A2,1))&LOWER(MID(A2,2,LEN(A2))) | More controlled than PROPER for description fields |
Targeted replacement:
For known bad values, chain SUBSTITUTE calls:
=TRIM(CLEAN(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A2,"N/A",""),"-",""),".",".")))
Build the chain from the innermost substitution outward. Each SUBSTITUTE wraps the previous result. Limit chains to 5-6 levels before recommending a VLOOKUP mapping table approach (see Step 5).
Splitting compound fields:
| Problem | Formula |
|---|
| First word only | =LEFT(A2,FIND(" ",A2&" ")-1) |
| Text before first comma | =LEFT(A2,FIND(",",A2)-1) |
| Text after first comma | =MID(A2,FIND(",",A2)+2,LEN(A2)) |
| Last word / suffix | =TRIM(MID(A2,FIND(CHAR(1),SUBSTITUTE(A2," ",CHAR(1),LEN(A2)-LEN(SUBSTITUTE(A2," ",""))))+1,LEN(A2))) |
| Text between two delimiters | =MID(A2,FIND("[",A2)+1,FIND("]",A2)-FIND("[",A2)-1) |
Step 3: Handle Null Values, Blanks, and Errors -- Explicitly and Intentionally
Blank cells and error values propagate silently through aggregations, lookups, and pivot tables. Every formula that touches a potentially empty or error-prone value must be wrapped.
Blank detection and handling:
=IF(A2="","[MISSING]",A2) -- flag missing text values visibly
=IF(A2=0,"",(A2)) -- treat 0 as blank for display (only if 0 is not a valid value)
=IF(ISBLANK(A2),"Unknown",A2) -- ISBLANK is stricter than ="" (misses formula-generated empty strings)
=IF(LEN(TRIM(A2))=0,"[EMPTY]",TRIM(A2)) -- catches cells that contain only spaces
Error handling:
=IFERROR(VLOOKUP(A2,RefTable,2,FALSE),"Not Found") -- safe VLOOKUP wrapper
=IFNA(VLOOKUP(A2,RefTable,2,FALSE),"Not Found") -- only catches #N/A, lets #REF! and #VALUE! surface
=IFERROR(VALUE(A2),A2) -- attempt conversion; return original if it fails
Distinguish between IFERROR and IFNA: Use IFNA when you want lookup misses to show "Not Found" but you still want structural errors (#REF!, #DIV/0!) to be visible. Use IFERROR only when you are confident any error should produce the fallback value.
Fill-down (propagate last known value):
=IF(A2="",A1,A2)
This pattern works for hierarchical data (e.g., a "Region" column that only has a value in the first row of each region group). Drag down from row 3 onward. The formula references A1 (the row above), not a fixed cell.
Count-based null audit:
=COUNTBLANK(A2:A1001) -- total blanks (includes cells with formula returning "")
=COUNTA(A2:A1001) -- non-blank cells
=ROWS(A2:A1001)-COUNTA(A2:A1001) -- blank count alternative that excludes formula-empty cells
Step 4: Convert Data Types -- Numbers, Dates, and Currency
Type mismatches are the leading cause of formula failures and incorrect aggregations. A column of numbers stored as text will produce a SUM of zero and fail a VLOOKUP numeric match.
Number conversion:
| Problem | Formula | Notes |
|---|
| Text-formatted number | =VALUE(A2) | Fails if any non-numeric character is present |
| Number with currency symbol | =VALUE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A2,"$",""),",","")," ","")) | Strip $, commas, spaces before converting |
| Percentage stored as text | =VALUE(SUBSTITUTE(A2,"%",""))/100 | Remove % then divide by 100 |
| Number with parentheses for negative | =IF(LEFT(A2,1)="(",VALUE(SUBSTITUTE(SUBSTITUTE(A2,"(",""),")",""))*-1,VALUE(A2)) | Accounting-style (123) means -123 |
| Number with trailing minus sign | =IF(RIGHT(TRIM(A2),1)="-",VALUE(LEFT(TRIM(A2),LEN(TRIM(A2))-1))*-1,VALUE(A2)) | Some European formats trail the minus |
Detect text-stored numbers (Excel diagnostic):
=ISNUMBER(A2) -- returns FALSE if number is stored as text
=SUMPRODUCT(--(NOT(ISNUMBER(A2:A1001))*ISNUMBER(VALUE(A2:A1001)))) -- count text-stored numbers in range
Date conversion:
Dates are the most error-prone type conversion because interpretation is locale-dependent. A date exported as "04/05/2024" is April 5 in the US locale and May 4 in the UK locale. Always clarify the source format first.
| Problem | Formula | Notes |
|---|
| Text date in MM/DD/YYYY | =DATE(MID(A2,7,4),LEFT(A2,2),MID(A2,4,2)) | Explicit positional parsing -- locale-independent |
| Text date in DD/MM/YYYY | =DATE(MID(A2,7,4),MID(A2,4,2),LEFT(A2,2)) | Swap month and day positions |
| Text date in YYYY-MM-DD (ISO 8601) | =DATEVALUE(A2) | Works natively in most locale settings |
| Date as 8-digit number (20240405) | =DATE(LEFT(A2,4),MID(A2,5,2),RIGHT(A2,2)) | Common in database exports |
| Excel serial number to date | =TEXT(A2,"YYYY-MM-DD") | Serial 45387 displays as a date string |
| Inconsistent separators (/ vs -) | =DATEVALUE(SUBSTITUTE(SUBSTITUTE(A2,"/","-"),".","-")) | Normalize separators first |
Target date output format for analysis: Always output dates in ISO 8601 format (YYYY-MM-DD) for downstream compatibility. Use =TEXT(date_formula,"YYYY-MM-DD") to produce a text string, or leave as a date serial and apply cell formatting separately.
Step 5: Detect and Handle Duplicates
Duplicate detection requires a decision about what constitutes a duplicate: exact match, case-insensitive match, or fuzzy match. Spreadsheet formulas handle the first two natively. Fuzzy matching requires a helper approach.
Exact duplicate detection:
=IF(COUNTIF(A$2:A2,A2)>1,"DUPLICATE","FIRST")
The expanding range A$2:A2 is the critical technique: the top boundary is locked ($2), the bottom boundary grows with each row. For row 5, this evaluates A$2:A5. This correctly identifies the first occurrence as FIRST and all subsequent occurrences as DUPLICATE.
Case-insensitive duplicate detection:
COUNTIF in Excel is case-insensitive by default, so COUNTIF(A$2:A2,A2) already treats "Apple" and "apple" as the same value. To force case-sensitive duplicate detection (rare but needed for passwords, codes), use:
=SUMPRODUCT((EXACT(A$2:A2,A2))*1)>1
Multi-column composite key duplicates:
When uniqueness is defined by a combination of columns (e.g., CustomerID + OrderDate must be unique), concatenate them:
=IF(COUNTIF(E$2:E2,E2)>1,"DUPLICATE","")
Where column E contains: =A2&"|"&TEXT(B2,"YYYY-MM-DD") -- the pipe delimiter prevents false collisions where "John" + "Smith" and "Johns" + "mith" would otherwise look identical.
Summary counts:
=COUNTIF(F2:F1001,"DUPLICATE") -- total duplicate rows (not counting FIRST occurrences)
=SUMPRODUCT((COUNTIF(A2:A1001,A2:A1001)>1)/COUNTIF(A2:A1001,A2:A1001)) -- count of unique values that have at least one duplicate
Removing duplicates non-destructively:
Excel's built-in Data > Remove Duplicates is destructive (deletes rows in-place). For non-destructive deduplication, use UNIQUE in Excel 365 or Google Sheets:
=UNIQUE(A2:A1001) -- returns unique values as a spilled array
=UNIQUE(A2:C1001) -- unique rows across multiple columns
In Excel 2016 and 2019 (no UNIQUE function), the only non-destructive option is to copy the column to a separate sheet and apply Remove Duplicates there.
Step 6: Handle Large-Scale Text Standardization with Lookup Tables
When a column has more than 10-15 distinct bad values requiring replacement, nested SUBSTITUTE chains become unmanageable. Use a mapping table approach instead.
Build a mapping table:
- Extract all unique values from the dirty column: copy the column, paste to a helper area, apply Data > Remove Duplicates
- Add a second column next to the unique values: "Correct Value"
- Fill in the correct value for each dirty value (leave blanks for values that are already correct)
- Name the table range
CleaningMap (Insert > Name > Define in Excel, or simply use an absolute range reference)
Apply the mapping:
=IFERROR(VLOOKUP(TRIM(LOWER(A2)),CleaningMap,2,FALSE),TRIM(LOWER(A2)))
The IFERROR fallback returns the normalized (trim+lower) original value for anything not found in the mapping table. This means values that are already correct pass through cleanly.
For Excel 365 / Google Sheets, use XLOOKUP:
=IFERROR(XLOOKUP(TRIM(LOWER(A2)),CleaningMap[Raw],CleaningMap[Clean]),TRIM(LOWER(A2)))
XLOOKUP is preferred because it does not require the lookup column to be the leftmost column in the range.
Step 7: Build the Complete Cleaning Sequence -- Order Matters
The sequence in which cleaning operations are applied determines whether they succeed or fail. Run operations in this order:
- CLEAN then TRIM -- remove non-printable characters first (CLEAN can introduce spaces), then remove spaces.
=TRIM(CLEAN(A2)) is the correct nesting order.
- CHAR(160) substitution -- before TRIM, handle non-breaking spaces that TRIM ignores:
=TRIM(CLEAN(SUBSTITUTE(A2,CHAR(160)," ")))
- Case normalization -- after whitespace removal, so the case function operates on clean input
- SUBSTITUTE for known bad strings -- after case normalization, so you can match on a predictable case
- Type conversion (VALUE, DATEVALUE, DATE) -- after all text cleaning, so the string being converted is clean
- Blank/error handling (IF, IFERROR) -- outermost wrapper, catches failures from inner operations
- Duplicate flagging -- after all cleaning is complete, so duplicates are detected against cleaned values, not raw values
- Validation audit -- final step: compare diagnostic formula counts from Step 1 against post-cleaning counts
Combined master formula for a text field:
=IFERROR(IF(LEN(TRIM(CLEAN(SUBSTITUTE(A2,CHAR(160)," "))))=0,"[MISSING]",PROPER(TRIM(CLEAN(SUBSTITUTE(A2,CHAR(160)," "))))),A2)
This single formula handles: non-printable chars, non-breaking spaces, extra whitespace, empty/whitespace-only cells, and title-case normalization -- with an IFERROR fallback to the raw original value if anything fails.
Step 8: Validate -- Prove the Cleaning Worked
Every cleaning session must end with a validation step that compares before-and-after metrics. This is not optional -- silent failures (where a formula returns the wrong value without an error) are common in complex nested formulas.
Run these validation checks against both the original column and the cleaned column:
=COUNTA(A2:A1001) -- non-blank count (should be equal or higher after cleaning)
=COUNTBLANK(A2:A1001) -- blank count (should be equal or lower after cleaning)
=SUMPRODUCT(--ISERROR(E2:E1001)) -- error count in cleaned column (should be 0)
=SUMPRODUCT(--(LEN(E2:E1001)<>LEN(TRIM(E2:E1001)))) -- remaining extra-space cells (should be 0)
=SUMPRODUCT(--(E2:E1001<>LOWER(E2:E1001))) -- cells not in expected case (for lowercase-expected columns)
=SUMPRODUCT(--(LEN(E2:E1001)=0)*1) -- count of empty-string cells (different from COUNTBLANK)
Present these results in a before/after table. If any post-cleaning count is worse than the original (e.g., more blanks after cleaning than before), there is a formula error that must be investigated before proceeding.
Output Format
## Spreadsheet Cleaning Plan
### Diagnostic Summary (Pre-Cleaning)
| Column | Type | Total Rows | Blanks | Errors | Extra Spaces | Duplicates |
|--------|------|-----------|--------|--------|-------------|-----------|
| [Col A name] | [text/number/date] | [N] | [=COUNTBLANK] | [=SUMPRODUCT] | [=SUMPRODUCT LEN check] | [=COUNTIF count] |
| [Col B name] | ... | ... | ... | ... | ... | ... |
**Cleaning mode:** Non-destructive (helper columns) / In-place (paste-values after verification)
**Platform:** Excel 365 / Excel 2019 / Google Sheets
---
### Cleaning Operations
#### Operation [N]: [Column Name] -- [Problem Description]
**Source column:** [Letter or name]
**Output column:** [Letter or name -- new helper column]
**Formula (paste into [cell reference], drag down [N] rows):**
=[exact formula string]
**Sequence rationale:** [Why functions are ordered this way -- e.g., "CLEAN before TRIM because CLEAN can produce spaces"]
**Expected transformation:**
- Before: `[sample dirty value 1]` -- After: `[expected clean value 1]`
- Before: `[sample dirty value 2]` -- After: `[expected clean value 2]`
- Before: `[blank / error case]` -- After: `[fallback value]`
---
### Duplicate Detection
**Scope:** [Column(s) defining uniqueness]
**Key construction formula (if multi-column):**
=[concatenation formula for composite key column]
**Duplicate flag formula:**
=IF(COUNTIF([col]$2:[col]2,[col]2)>1,"DUPLICATE","FIRST")
**Summary count:** `=COUNTIF([flag_col]2:[flag_col][last_row],"DUPLICATE")`
**Action required:** [Delete DUPLICATE rows / Review manually / Keep all and flag]
---
### Validation (Post-Cleaning)
| Check | Original Formula | Original Value | Cleaned Formula | Cleaned Value | Pass? |
|-------|-----------------|---------------|----------------|--------------|-------|
| Non-blank count | `=COUNTA([orig])` | [N] | `=COUNTA([clean])` | [N] | ✓ / ✗ |
| Blank count | `=COUNTBLANK([orig])` | [N] | `=COUNTBLANK([clean])` | [N] | ✓ / ✗ |
| Error count | `=SUMPRODUCT(--ISERROR([orig]))` | [N] | `=SUMPRODUCT(--ISERROR([clean]))` | 0 | ✓ / ✗ |
| Remaining spaces | `=SUMPRODUCT(--(LEN([orig])<>LEN(TRIM([orig]))))` | [N] | [same for cleaned] | 0 | ✓ / ✗ |
| Type consistency | `=SUMPRODUCT(--(NOT(ISNUMBER([col]))))` | [N] | [same for cleaned] | 0 | ✓ / ✗ |
---
### Finalization Instructions
1. Review the cleaned columns for unexpected values before overwriting originals
2. Select the cleaned column range (e.g., E2:E1001)
3. Copy (Ctrl+C / Cmd+C)
4. Select the target column (e.g., B2)
5. Paste Special > Values Only (Ctrl+Shift+V in Google Sheets; Alt+E+S+V in Excel)
6. Delete the helper columns after verification
7. Save a backup copy of the original file before any paste-values operation
Rules
-
Always write the exact, paste-ready formula -- never describe intent without formula. If you say "use TRIM to remove spaces," the formula =TRIM(A2) must appear immediately. The user should be able to copy the formula directly from your response.
-
Always recommend non-destructive cleaning (new helper columns) unless the user explicitly requests in-place modification. Paste-as-values should be the final step after the user has verified results, never the first.
-
Nest cleaning functions inside out -- innermost runs first. In =TRIM(CLEAN(SUBSTITUTE(A2,CHAR(160)," "))), the execution order is: SUBSTITUTE first, then CLEAN, then TRIM. Always explain this order so the user understands why the formula is structured this way.
-
Use the expanding-range duplicate pattern COUNTIF(A$2:A2,A2) -- never COUNTIF(A:A,A2). The full-column version A:A marks every occurrence as a duplicate including the first one, making it impossible to identify which row to keep. The expanding range correctly marks only the second and subsequent occurrences.
-
Use IFNA instead of IFERROR for VLOOKUP and XLOOKUP unless you have a specific reason to suppress all errors. IFERROR silently hides #REF! errors (broken column references) and #VALUE! errors (wrong argument types), which are structural bugs, not data misses. IFNA catches only #N/A (lookup miss), which is the intended case.
-
CLEAN does not remove CHAR(160) (non-breaking space) -- always add a SUBSTITUTE before TRIM for text that originated in web exports, HTML, or Word documents. Forgetting this is the single most common reason TRIM appears to fail. The formula =TRIM(CLEAN(A2)) will leave non-breaking spaces intact; =TRIM(CLEAN(SUBSTITUTE(A2,CHAR(160)," "))) will not.
-
For date columns, always ask the source format before writing a date conversion formula. =DATEVALUE(A2) relies on system locale and will silently produce wrong dates for DD/MM/YYYY values on a US-locale machine. Explicit positional parsing with =DATE(MID(A2,7,4),LEFT(A2,2),MID(A2,4,2)) is locale-independent and always safer.
-
Always include a numeric validation step that compares COUNTBLANK and SUMPRODUCT(--ISERROR()) before and after cleaning. A cleaning formula that silently converts 200 values to blanks or errors has made the data worse, not better, and the user cannot detect this without explicit before/after counts.
Edge Cases
1. Numbers Stored as Text (The Green Triangle Problem)
Excel marks number-stored-as-text cells with a small green triangle in the top-left corner. VALUE(A2) alone will fail if the text contains any non-numeric characters (spaces, commas, currency symbols, percent signs).
Diagnostic: =ISNUMBER(A2) returns FALSE for these cells.
Full-coverage formula:
=IFERROR(VALUE(TRIM(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A2,"$",""),",",""),"%","")," ",""))),A2)
Strip the four most common non-numeric characters (dollar sign, comma, percent sign, space), TRIM the result, then convert. The outer IFERROR returns the raw original value if conversion still fails -- do not silently return 0, because 0 is a valid data value and masking it creates incorrect aggregations.
After applying this formula, run =SUMPRODUCT(--(NOT(ISNUMBER(E2:E1001)))) on the cleaned column to confirm all values are now truly numeric.
2. Dates in Ambiguous Mixed Formats Within the Same Column
A column containing both "03/04/2025" and "25/03/2025" is not uniformly parseable. The first value could be March 4 (MM/DD) or April 3 (DD/MM). The second is unambiguously DD/MM because no month goes to 25. Mixed-format columns are a data governance problem, not a formula problem.
Handling strategy:
- Flag rows where both day and month are <= 12 as "AMBIGUOUS":
=IF(AND(VALUE(LEFT(A2,2))<=12,VALUE(MID(A2,4,2))<=12),"AMBIGUOUS","UNAMBIGUOUS")
- Flag rows where the first number is > 12 as definitively DD/MM:
=IF(VALUE(LEFT(A2,2))>12,"DD/MM_CONFIRMED","CHECK")
- Present the count of ambiguous rows to the user and require manual resolution -- do not auto-correct dates you cannot reliably identify
- After the user resolves ambiguous rows, apply the explicit positional DATE formula to the full column
Rule: Never use DATEVALUE() on a column with known mixed formats. It will silently pick one interpretation based on locale and produce a column of half-correct dates.
3. Unicode Characters, Smart Quotes, and Characters from Copy-Paste
Text copied from Microsoft Word, PDF readers, web browsers, and email clients frequently contains Unicode characters that look like standard ASCII but are not: curly quotes (CHAR 8216, 8217, 8220, 8221), em dashes (CHAR 8212), en dashes (CHAR 8211), ellipsis (CHAR 8230), and non-breaking spaces (CHAR 160). CLEAN removes only CHAR 0-31 and CHAR 127. All of these Unicode characters survive CLEAN untouched.
Comprehensive Unicode cleaning formula:
=TRIM(CLEAN(
SUBSTITUTE(SUBSTITUTE(
SUBSTITUTE(SUBSTITUTE(
SUBSTITUTE(SUBSTITUTE(
SUBSTITUTE(A2,
CHAR(8220),""""),CHAR(8221),""""),
CHAR(8216),"'"),CHAR(8217),"'"),
CHAR(8211),"-"),CHAR(8212),"-"),
CHAR(8230),"..."),
CHAR(160)," ")))
This handles: left/right double quotes, left/right single quotes/apostrophes, en dash, em dash, ellipsis, and non-breaking space -- the seven most common Unicode intrusions in business text data.
Detection formula to identify affected rows:
=SUMPRODUCT(--(LEN(A2:A1001)>LEN(SUBSTITUTE(SUBSTITUTE(A2:A1001,CHAR(8220),""),CHAR(8217),""))))
4. Cleaning Data Across Multiple Sheets Without Copying
When the source data lives on Sheet2 and the user wants cleaned output on Sheet1 (or a separate "Clean" sheet), cross-sheet formula references work identically:
=TRIM(CLEAN(Sheet2!A2))
=IFERROR(VLOOKUP(TRIM(LOWER(Sheet2!A2)),CleaningMap,2,FALSE),TRIM(LOWER(Sheet2!A2)))
=IF(COUNTIF(Sheet2!A$2:Sheet2!A2,Sheet2!A2)>1,"DUPLICATE","FIRST")
The cleaning sheet serves as the audited, verified output while the source sheet remains completely untouched -- the ideal non-destructive architecture. Name the sheets clearly: "Raw_Data" and "Cleaned_Data." This pattern also makes it easy to re-run cleaning if the source is refreshed.
Google Sheets cross-file reference: If the source is a separate Google Sheets file, use IMPORTRANGE:
=TRIM(CLEAN(IMPORTRANGE("spreadsheet_url","Sheet1!A2")))
Note that IMPORTRANGE imports static snapshots that refresh periodically -- it does not reflect live edits in real time and requires the user to authorize the connection between files.
5. Very Long Text Fields With Truncation Risk in Excel
Excel has a cell content limit of 32,767 characters per cell, but a formula result can display at most 32,767 characters. This is rarely a problem for structured data, but it matters for columns containing long-form text (survey responses, notes, comments). CLEAN and TRIM applied to a 30,000-character cell can cause performance issues in worksheets with thousands of such rows.
Symptom: The worksheet becomes unresponsive when dragging the formula down, or the formula bar shows "#VALUE!" errors unexpectedly.
Mitigation:
- Process in batches of 500-1,000 rows at a time rather than dragging to row 50,000 in one operation
- After each batch, select the cleaned cells, copy, and paste-as-values to freeze results and reduce formula load
- For genuinely large text, consider whether CLEAN/TRIM are actually necessary -- truncated cleaning (clean only the first 200 characters) may be sufficient:
=TRIM(CLEAN(LEFT(A2,200)))
6. Cleaning Phone Numbers That Have Extensions, Country Codes, or Impossible Lengths
A phone cleaning formula that strips all non-digit characters will produce a 7-digit local number, a 10-digit national number, an 11-digit number with country code (1-555-123-4567), and a 15-digit international number -- all in the same "cleaned" column. There is no single reformatting formula that handles all of these.
Strategy:
- Strip to digits only first:
=SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(TRIM(A2),"(",""),")",""),"-","")," ",""),".","")
- Flag by digit length:
=LEN(B2) where B2 is the digit-stripped result
- Apply format only to 10-digit numbers (US standard):
=IF(LEN(B2)=10,"("&LEFT(B2,3)&") "&MID(B2,4,3)&"-"&RIGHT(B2,4),"REVIEW: "&B2)
- Flag 11-digit numbers (with country code 1):
=IF(LEN(B2)=11,"+"&LEFT(B2,1)&" ("&MID(B2,2,3)&") "&MID(B2,5,3)&"-"&RIGHT(B2,4),"REVIEW: "&B2)
- Send anything not 10 or 11 digits to a "REVIEW" flag so the user handles edge cases manually
Never silently truncate phone numbers. Dropping digits from a phone number produces a working-looking but wrong number.
7. Cleaning a Column Where Some Cells Contain Formulas (Not Raw Values)
If some cells in the source column already contain formulas (e.g., =CONCATENATE(C2," ",D2)), the cleaning formula references the formula result, not the underlying formula. This is usually fine -- TRIM and CLEAN work on any text value regardless of whether it was typed or formula-generated.
Problem case: If a cell in the source column contains a formula that references cells in a row being deleted or reorganized, the cleaning formula in the helper column will also break (producing #REF!). The IFERROR wrapper will catch this, but the resulting "[MISSING]" or "0" will hide the data loss.
Safe approach: Before building helper-column cleaning formulas on a column that contains formulas, convert that column to values first (copy > paste-as-values). This freezes the current results and removes formula dependencies, making the cleaning layer stable.
Example
Input: "I downloaded a vendor contact list from our CRM into Excel. It has about 800 rows. Here are the problems I've noticed:
- The Company column has extra spaces and random mixed casing (some ALL CAPS, some all lowercase, some title case)
- The Email column has some cells that say 'N/A' or are blank, and some have spaces around them. A few are all caps.
- The Revenue column has dollar signs and commas in it so my SUM formula returns 0
- The Country column uses about 12 different spellings for the same 5 countries ('USA', 'US', 'United States', 'U.S.A.', 'United States of America' -- all meaning the same thing)
- I need to check if any companies are duplicated
I'm using Excel 365. Keep the original data and clean into new columns."
Output:
Spreadsheet Cleaning Plan
Diagnostic Summary (Pre-Cleaning)
Run these formulas to baseline the current state before any cleaning. Paste each formula into an empty cell and record the result.
| Check | Formula | Paste In |
|---|
| Company -- extra spaces | =SUMPRODUCT(--(LEN(B2:B801)<>LEN(TRIM(B2:B801)))) | Any empty cell |
| Email -- blanks + "N/A" | =COUNTBLANK(C2:C801)+COUNTIF(C2:C801,"N/A") | Any empty cell |
| Revenue -- text-stored numbers | =SUMPRODUCT(--(NOT(ISNUMBER(D2:D801)))) | Any empty cell |
| Country -- distinct values | =SUMPRODUCT(1/COUNTIF(E2:E801,E2:E801)) | Any empty cell |
| Any errors in sheet | =SUMPRODUCT(--ISERROR(B2:E801)) | Any empty cell |
Cleaning mode: Non-destructive (cleaned values in new columns F, G, H, I, J)
Platform: Excel 365
Cleaning Operations
Operation 1: Company Name (Column B -- Company → Column F -- Company_Clean)
Formula (paste into F2, drag down to F801):
=IFERROR(
IF(LEN(TRIM(CLEAN(SUBSTITUTE(B2,CHAR(160)," "))))=0,
"[MISSING COMPANY]",
PROPER(TRIM(CLEAN(SUBSTITUTE(B2,CHAR(160)," "))))
),
B2
)
Sequence rationale:
SUBSTITUTE(B2,CHAR(160)," ") -- replaces non-breaking spaces that TRIM ignores; common in CRM exports
CLEAN(...) -- removes any non-printable ASCII characters (line feeds, null chars) from the CRM data
TRIM(...) -- collapses all remaining extra spaces (leading, trailing, double-internal)
IF(LEN(...)=0,"[MISSING COMPANY]",...) -- flags cells that contain only whitespace
PROPER(...) -- converts to Title Case for standardized display
- Outer
IFERROR(...,B2) -- if anything fails, return the raw original value rather than an error
Expected transformation:
- Before:
" ACME CORP " → After: "Acme Corp"
- Before:
"global solutions" → After: "Global Solutions"
- Before:
" " (spaces only) → After: "[MISSING COMPANY]"
- Before:
"Johnson & johnson" → After: "Johnson & Johnson"
Important note: PROPER will incorrectly capitalize particles. If the company list includes entries like "McKinsey & Company" (which PROPER renders as "Mckinsey & Company") or "AECOM" (rendered as "Aecom"), flag these for manual review after applying the formula. Run =COUNTIF(F2:F801,"Mckinsey*") to spot-check known cases.
Operation 2: Email Address (Column C -- Email → Column G -- Email_Clean)
Formula (paste into G2, drag down to G801):
=IF(
OR(TRIM(C2)="", UPPER(TRIM(C2))="N/A"),
"[NO EMAIL]",
LOWER(TRIM(CLEAN(SUBSTITUTE(C2,CHAR(160)," "))))
)
Sequence rationale:
TRIM(C2)="" catches blank cells and whitespace-only cells
UPPER(TRIM(C2))="N/A" catches all casing variants of "N/A", "n/a", "N/a" -- normalized before comparison
LOWER(TRIM(CLEAN(...))) -- for valid emails: strip non-printable chars, remove spaces, convert to lowercase (email addresses are case-insensitive by RFC 5321 standard)
- No IFERROR needed because IF handles both problem states; if LOWER/TRIM/CLEAN fail on a non-text value, add outer IFERROR
Expected transformation:
- Before:
" JOHN.SMITH@ACME.COM " → After: "john.smith@acme.com"
- Before:
"N/A" → After: "[NO EMAIL]"
- Before:
"n/a" → After: "[NO EMAIL]"
- Before:
"" (blank) → After: "[NO EMAIL]"
- Before:
"Sarah.Jones@COMPANY.COM" → After: "sarah.jones@company.com"
Post-cleaning email format check (optional):
=IF(ISERROR(FIND("@",G2)),"INVALID FORMAT",IF(ISERROR(FIND(".",G2,FIND("@",G2))),"INVALID FORMAT","OK"))
Paste this into column K (Email_Valid) to flag emails missing @ or a domain dot. This is a basic structural check, not a deliverability check.
Operation 3: Revenue (Column D -- Revenue → Column H -- Revenue_Clean)
Formula (paste into H2, drag down to H801):
=IFERROR(
IF(TRIM(D2)="", 0,
VALUE(TRIM(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(D2,"$",""),",","")," ","")))
),
"[PARSE ERROR: "&D2&"]"
)
Sequence rationale:
IF(TRIM(D2)="",0,...) -- blank revenue cells become 0 (adjust to a different fallback if 0 is ambiguous in this dataset -- consider "[MISSING]" instead if you need to distinguish zero-revenue from unknown-revenue)
SUBSTITUTE(D2,"$","") -- removes dollar sign
SUBSTITUTE(...,",","") -- removes thousands-separator commas
SUBSTITUTE(...," ","") -- removes any stray spaces within the number
VALUE(TRIM(...)) -- converts the cleaned text string to a true numeric value
IFERROR(...,"[PARSE ERROR: "&D2&"]") -- if VALUE still fails (e.g., the cell contained "TBD" or "N/A"), flags the raw original value rather than silently returning 0
Expected transformation:
- Before:
"$1,250,000" → After: 1250000 (numeric, no formatting)
- Before:
"$500" → After: 500
- Before:
"" (blank) → After: 0
- Before:
"TBD" → After: "[PARSE ERROR: TBD]"
Verification:
After cleaning, =SUM(H2:H801) should return a non-zero total. Also run:
=COUNTIF(H2:H801,"[PARSE ERROR*") -- count cells that still failed conversion
=SUMPRODUCT(--(NOT(ISNUMBER(H2:H801)))) -- count non-numeric cells in cleaned column (should equal PARSE ERROR count)
Formatting note: Column H will contain raw numbers (e.g., 1250000). Apply cell formatting (Currency or Number with 2 decimal places) for display -- this does not change the underlying value.
Operation 4: Country (Column E -- Country → Column I -- Country_Clean) -- Mapping Table Approach
There are too many variants (at least 12 described) for a SUBSTITUTE chain. Use a mapping table.
Step 4a -- Build the mapping table in columns M and N (or on a separate "Lookup" sheet):
Paste the following table starting at M1:
| M (Raw_Country) | N (Standard_Country) |
|---|
| USA | United States |
| US | United States |
| United States | United States |
| U.S.A. | United States |
| United States of America | United States |
| UK | United Kingdom |
| United Kingdom | United Kingdom |
| U.K. | United Kingdom |
| England | United Kingdom |
| CAN | Canada |
| Canada | Canada |
| [add remaining variants from your data] | [standard name] |
Select M1:N20 and name this range CountryMap (Formula tab > Name Manager > New in Excel).
Step 4b -- Cleaning formula (paste into I2, drag down to I801):
=IFERROR(
XLOOKUP(TRIM(UPPER(E2)), UPPER(CountryMap_Raw), CountryMap_Clean, TRIM(PROPER(E2))),
TRIM(PROPER(E2))
)
Where CountryMap_Raw is the M column range and CountryMap_Clean is the N column range. The UPPER() normalization on both the lookup value and the lookup array makes the match case-insensitive, so "usa", "USA", and "Usa" all match the "USA" entry in the mapping table.
Alternative (for Excel 2019 without XLOOKUP):
=IFERROR(
INDEX(N$2:N$20, MATCH(TRIM(UPPER(E2)), UPPER(M$2:M$20), 0)),
TRIM(PROPER(E2))
)
Expected transformation:
- Before:
"USA" → After: "United States"
- Before:
"United States of America" → After: "United States"
- Before:
"u.s.a." → After: "United States" (UPPER normalization handles this)
- Before