Still Duplicated After Deduping? Five Data Cleaning Traps
Lists pasted out of Excel, a log file or a chat window look clean and still leave duplicates behind. The cause is almost always an invisible difference - how lines are compared, what shape the characters take, and how sorting compares code points.
- Duplicate detection works on whole lines, so two lines count as the same only after trimming, with no similarity matching and no ignoring of punctuation or word order
- Both comparison and output trim each line, which means pasting indented text loses its structure - code and YAML should not be cleaned this way
- Plain sorting compares UTF-16 code points, and uppercase letters sort before lowercase, so Apple lands before apple
- Natural sorting splits each line into digit and non-digit runs, which puts item2 before item10 - the exact opposite of plain sorting
- Chinese sorts by Unicode code point rather than pinyin or stroke count, so pinyin order needs transliteration before sorting
#Work out which kind of duplicate you have
"Still duplicated after deduping" means one of two things: the comparison rule never covered the difference, or the duplicate is not inside a single line. Treating those as one problem makes every tool look broken.
The test is simple: copy both suspect lines and compare them character by character.
| Symptom | Real cause | What to do |
|---|---|---|
| Two identical-looking lines both survive | Trailing space, fullwidth characters, case | Trim and normalise fullwidth, then dedupe |
| The same entity appears twice, spelled differently | Information spans two fields, not one line | Field-level matching; no text tool can do it |
| Line count did not change at all | Every line is unique (timestamps, counters) | Drop the irrelevant column first |
| Blank lines counted as entries | The blank-line filter is off | Confirm the filter is enabled |
#Whole-line comparison: what counts as a duplicate
Comparison works on whole lines: two lines are duplicates only when they match exactly after trimming. There is no similarity matching, punctuation is not ignored, and word order matters - "apple banana" and "applebanana" are two different lines.
With the text deduplicator, three behaviours decide the output:
- Both comparison and output trim, so leading and trailing spaces disappear
- Order follows first appearance and is never re-sorted alphabetically
- With case-insensitive mode on, the first occurrence's casing wins:
Applefirst meansApplein the output
So the result is a clean list in first-appearance order with original casing. For alphabetical or numeric order, run the sorter afterwards.
The blank-line filter is on by default, so blank lines at either end are silently removed. That matters more for indented content: pasting code, a Markdown list or YAML, the indentation is trimmed away and the structure breaks. Do not clean structured text with a dedupe tool.
#Deduping will touch your indentation
Trimming is necessary for deduping - without it a trailing space creates a false duplicate - and the price is losing indentation. That trade is exactly right for lists and exactly wrong for code.
The test: does the indentation in this text carry meaning?
- Lists, tags, IDs, URLs: indentation is decorative, safe to use
- Code, YAML, Markdown lists, SQL: indentation is syntax, using this tool breaks it
For structured text, dedupe inside the editor, or use a tool that compares trimmed lines but preserves them. This is also why general text tools never replace formatters - they care about different things.
#Sorting: why A comes before a
Plain sorting compares UTF-16 code points, and uppercase letters have lower code points than lowercase (A is 65, a is 97), so Apple sorts before apple. Not a bug - an inevitable result of code point comparison.
Three cases with the text sorter:
- Case should not affect order: enable case-insensitive sorting
- Chinese: sorts by Unicode code point, not pinyin or stroke count. Pinyin order needs transliteration first
- Sorting is stable: equal keys keep their original relative order instead of shuffling
The sort key is the trimmed line, and the output is that same version, so outer whitespace disappears. With the blank-line filter off, blank lines sort as empty strings and pile up at the top in ascending order.
Order of operations: dedupe first, then sort. The reverse still works, but duplicates cluster together and make the list harder to read, and "keep the first occurrence" is the semantic most people expect.
#What natural sorting fixes
Plain sorting puts item10 before item2 because it compares character by character and 1 is less than 2. Human reading expects the opposite, which is why natural sorting exists.
Natural sorting splits each line into alternating digit and non-digit runs and compares digit runs by numeric value:
| Line | Plain order | Natural order |
|---|---|---|
| item2 | 2nd | 1st |
| item10 | 1st | 2nd |
| v1.9 | after v1.10 | before v1.10 |
Version numbers, page numbers, numbered products and chapter titles all want natural sorting. Plain names and addresses are fine with plain sorting - no digit runs means identical results.
#Bulk replacement: what regex is left for
Deduping and sorting only handle whole lines; changes inside a line need replacement. This comes up constantly when cleaning tables copied from a web page: extra tabs, bracketed notes, a uniform prefix.
Validate the pattern with the regex tester, then run it through find and replace:
# Strip a trailing marker such as " (archived)" from each line
Find: \s*\(archived\)$
Replace: (leave empty)Three frequent regex traps:
.does not match newlines: use thesflag or[\s\S]*?for multi-line spans^and$are not per-line by default: enable multi-line mode\ddoes not match fullwidth digits:123pasted from Excel will not match, convert it first
A dollar sign in the replacement is always special regardless of mode, so a literal one needs $$.
Replacement is a single pass and never re-runs over its own output, so swapping A and B cannot loop - but it also cannot be done in one step. Use a temporary placeholder, then replace a second time.
#Which tool to use
| What you need | Tool | Key difference |
|---|---|---|
| Remove duplicate list lines | Text deduplicator | Whole-line, trims indentation away |
| Sort alphabetically or numerically | Text sorter | Code point order, uppercase first |
| Sort versions and page numbers the way people read them | Text sorter | Natural mode compares digit runs numerically |
| Reverse line or character order | Text reverser | Four modes; use per-line for multi-line text |
| Confirm line and word counts | Text counter | Counts code points, separates characters from bytes |
| Validate a replacement pattern | Regex tester | Supports g/i/m/s/u flags |
| Run a bulk replacement | Find and replace | Plain mode escapes metacharacters for you |
Suggested cleaning sequence: count lines for a baseline, normalise fullwidth characters, drop blank lines and dedupe, sort if needed, then finish with in-line replacements. Keep each intermediate result so a bad step is easy to identify.
#Frequently asked questions
#Two lines look identical but both survive. Why?
Compare character by character first; a trailing space or tab is the usual culprit. If it is not that, look for fullwidth versus halfwidth forms (123 versus 123), non-breaking spaces (a pasted becomes U+00A0 and looks like an ordinary space), or zero-width characters. Checking the character count with the text counter is the fastest way to spot them.
#Why is Chinese not sorted by pinyin?
Because sorting uses Unicode code points, which have nothing to do with pronunciation. Pinyin order requires transliterating each character, sorting the pinyin strings, then mapping back - native browser sorting cannot do it.
#Why did the line count grow after deduping?
Check whether "show counts" is enabled: it prefixes every line with a count and two spaces, so the output is no longer pure data. Feeding that back in treats each annotated line as a new, unique entry.
#How do I delete only blank lines?
The deduplicator removes blank lines by default. To remove only blank lines while keeping duplicates, use find and replace in regex mode: find ^\s*$\n? and replace with nothing, with multi-line mode enabled.
#Reversing turned my Chinese into garbage.
Wrong mode. Character reversal reverses newlines too, so lines get interleaved and misaligned, and it splits by code point, which breaks multi-code-point sequences such as skin-toned gestures and family emoji joined by zero-width joiners. For "write each line backwards", use per-line reversal.
Tools used in this guide
Remove duplicate lines, ignore case and blank lines
Open tool →Text SorterSort lines ascending/descending with natural number sort
Open tool →Text ReverserReverse characters, lines, inline and words
Open tool →Regex TesterReal-time regex matching with highlight and error hints
Open tool →Word CounterCount words, characters, lines, paragraphs and bytes as you type. Free, no si…
Open tool →