Skip to main content
Learning Center
Spreadsheets for Decisions

Clean data before deciding

Find duplicates and inconsistent records

Tell an exact duplicate from a legitimate repeat, find the row that looks identical and is not, and log every cleanup decision instead of deleting.

Lesson 5 of 18 in the recommended order · About 30 min (estimate)

On this page

Outcome

Distinguish an exact duplicate record from a legitimate repeat of the same person, build a comparison key that survives spacing differences, recognise a value it cannot repair, and log a keep, drop or repair decision for each row without removing anything from the source.

Why it matters

"Remove duplicates" is the most confidently destructive button in a spreadsheet. It does exactly what it says, against a definition of "duplicate" you did not write and probably would not agree with.

Two rows for the same person are not a duplicate if that person came to two sessions. Two rows with the same name are not the same person if there are two Ana Ruizes. And two rows that look character-for-character identical on screen can be genuinely different values, because one of them contains something you cannot see.

None of that is unusual data. It is what a signup sheet filled in by three volunteers over a week looks like. What separates a usable cleanup from a damaging one is not a better button; it is deciding, in writing, what a duplicate is for this data, before removing anything.

Concept

Start by being precise about what is being compared.

A duplicate is a repeated record, not a repeated name. A record is the combination of fields that identifies the thing that happened: for a signup, the person, the session and the date. Two rows are the same record when all three match. If the person matches and the session does not, that is two attendances, and merging them deletes one.

So the first job is to build a record key: the identifying fields joined into one value that can be compared. Joining them with a separator that cannot appear in the data — a vertical bar is a common choice — keeps AB|C from colliding with A|BC.

Before that key can be compared, the parts have to be comparable. Two things are usually in the way, and only one of them is the one people expect.

Case is usually not the problem. A spreadsheet's own text comparison ignores case. In this grid, as in Excel and Google Sheets, "ana ruiz" = "Ana Ruiz" is TRUE, and COUNTIF counts them together. Levelling case with UPPER or LOWER is still a good habit — it makes the key stable if it ever leaves the spreadsheet for a system that does care — but it is not what makes those two rows match here. They already match.

Spacing is the problem. "Ana Ruiz " with a trailing space is a different value from "Ana Ruiz", and "Ana Ruiz" with two spaces in the middle is a third. None of them matches either of the others. TRIM removes leading and trailing spaces and collapses runs of internal spaces to one, which repairs all three at once. It is the necessary half of a comparison key; case levelling is the optional half.

If you need case to matter — comparing codes where AB1 and ab1 are different things — use EXACT, which is case-sensitive here and in both products.

And then there is the character you cannot see. Text pasted from a web page or a document often contains a non-breaking space instead of an ordinary one. It looks exactly like a space. It is not one, and TRIM does not remove it — not here, and not in Excel, which is the behaviour this grid deliberately reproduces. A row like that fails to match anything, silently, and the only way to notice is that two counts disagree: how many rows a reader says are this person, against how many rows the formula matches.

That disagreement is worth building into the sheet as a number, because it is the only alarm you will get.

Finally: do not delete. Decide. Add a column that says keep, drop or repair for each row, with the source sheet untouched beside it. A dropped row is still there to look at when somebody asks why the total changed. A deleted row is gone, and so is the evidence that the deletion was wrong.

Worked example

Read the code

Ten signups, entered by three volunteers:

S-01  Ana Ruiz        2026-09-01  CR-101
S-02  ana ruiz        2026-09-02  CR-102
S-03  Ana  Ruiz       2026-09-03  CR-105     two spaces, and one on the end
S-04  Ben Okafor      2026-09-01  CR-101
S-05  Ben Okafor      2026-09-01  CR-101     entered twice
S-06  Cai Bell        2026-09-02  CR-102
S-07  Cai Bell        2026-09-04  CR-106     a second session, not a duplicate
S-08  Ana Ruiz        2026-09-04  CR-106     pasted from a web page
S-09  Dev Nakamura    2026-09-03  CR-105
S-10  Dev Nakamura    2026-09-03  CR-105     entered twice

Four rows say Ana Ruiz. Three of them differ only in case and spacing, and all three describe different sessions, so all three are real attendances that happen to share a person. The fourth, S-08, is also a real attendance — and its name holds a non-breaking space where the ordinary one should be, so its name will not match the others using only TRIM and case levelling.

S-05 and S-10 are the only exact duplicates here: same person, same session, same day, entered a second time by somebody who did not know it had already been entered.

S-07 is the trap. It is Cai Bell again, and it is a different session on a different day. A tool that deduplicates on the name alone deletes an attendance that happened.

Compare duplicate records with legitimate repeats

Same name is not the same record

Identity rule for this fixture: Compare the person, date and session together. The row ID records which source row supplied the entry; it is not part of the attendance identity.

Names identify the people in this small example. In real data, shared names require stronger identity evidence or a stable person ID. Keep the source unchanged.

Ben · one attendance entered twice

  • S-04

    Ben Okafor · 2026-09-01 · CR-101

  • Same person + same date + same session

    S-05

    Ben Okafor · 2026-09-01 · CR-101

Normalized person
BEN OKAFOR in both rows
Actual formula key
BEN OKAFOR|46266|CR-101 in both rows
Date representation
46266 is the stored date serial for the displayed 2026-09-01.
Decision in source order
Keep S-04; mark S-05 drop. Two rows belong to this duplicate group; only one is an excess copy.

Different row IDs do not make these two entries different attendances under the stated rule.

Cai · two legitimate attendances

  • S-06

    Cai Bell · 2026-09-02 · CR-102

  • Same person; different date and session

    S-07

    Cai Bell · 2026-09-04 · CR-106

Normalized person
CAI BELL in both rows
S-06 formula key
CAI BELL|46267|CR-102
S-07 formula key
CAI BELL|46269|CR-106
Decision
Keep both. Each complete key occurs once; matching the name alone would lose one attendance.

The dates display as text in the comparison strips; the actual concatenation uses the numeric date values shown in the keys.

These are only the already-explained Ben and Cai examples. The spacing and pasted-character cases remain for the practice tasks. Keep/drop labels describe the working copy; no source row is deleted.

Predict the output

Given the rows above, predict these four before revealing them. B2 holds Ana Ruiz, B3 holds ana ruiz, B4 holds Ana Ruiz and B9 holds the pasted version.

=B2=B3
=EXACT(B2,B3)
=TRIM(B4)=B2
=TRIM(B9)=B2
Show the four answers

=B2=B3 is TRUE. This is the one most people get wrong. A spreadsheet's equals sign ignores case, so a name typed in lower case already matches one typed in title case. Nothing needs to be done about it for the comparison to work.

=EXACT(B2,B3) is FALSE. EXACT is the case-sensitive comparison. Reach for it when case is meaningful; the ordinary equals sign never is.

=TRIM(B4)=B2 is TRUE. TRIM collapses the double space and removes the trailing one, and the result matches.

=TRIM(B9)=B2 is FALSE, and this is the finding. The name in B9 looks identical on screen, TRIM has been applied to it, and it still does not match — because the character between the words is a non-breaking space, and TRIM removes ordinary spaces only.

Two of these four results are about case and neither of them is a problem. One is about spacing and TRIM fixes it. One is about a character nothing on screen can show you, and it is the only one that will still be wrong tomorrow.

Modify the code

A first attempt at finding duplicates compares names:

=COUNTIF($B$2:$B$11,B2)

Over these rows it reports 2 for both Cai Bell rows and 2 for both Dev Nakamura rows, and treats them the same way. They are not the same: Dev's pair is one signup entered twice, Cai's pair is two signups.

Compare the record instead of the name:

=F2&"|"&C2&"|"&D2

where F holds the normalised name. Now Cai's two rows have different keys, because their dates and sessions differ, and Dev's two rows have the same key. Counting over that column separates the two cases without any judgement being needed:

=COUNTIF($G$2:$G$11,G2)

One more step turns "these rows are duplicates" into "this row is the one to drop". A COUNTIF whose range grows as it is copied down counts occurrences up to and including the current row:

=IF(COUNTIF($G$2:G2,G2)>1,"drop","keep")

The start of the range is anchored and the end is not, so in row 2 it looks at G2:G2, in row 6 at G2:G6, and so on. The first copy of a record sees a count of 1 and is kept; every later copy sees more and is dropped. Exactly one row of each pair survives, and which one is a rule rather than a mood.

Debug the bug

A colleague ran Remove Duplicates over the attendance list. It reported that 31 rows were removed, and the term's attendance total fell by 402. They ask whether that is right.

Those two numbers alone do not establish whether the removal was right. Check the record identities and the formula behind the attendance total.

  1. Ask what it compared. Remove Duplicates deduplicates on the columns you tick. If only the name column was ticked, it kept one row per person for the whole term. That is not a duplicate removal; it is a conversion of an attendance log into a membership list.
  2. Reconstruct the excess copies from the source. On an untouched copy, build the record key and count copies after the first in each group, not every row belonging to a repeated group. Then compare those specific row IDs with the removed rows. Even a matching total of 31 is not proof that the right rows were removed.
  3. Recompute the total using its actual definition. A reduction of 402 across 31 rows averages about 12.97 per row. If the total is simply a count of rows, removing 31 rows reduces it by 31. If it sums a numeric attendance field, removing genuine duplicates can reduce that sum by 402. The size of the change alone cannot distinguish correct cleanup from lost records.
  4. Check for lost legitimate repeats. Compare the removed row IDs with the source. The same person in a different session is a different record under this definition; a name-only key would merge those records.
  5. If the cleanup was wrong, restore from the source and start again with a key. Do not try to re-add missing rows by hand. This is what the untouched sheet is for.

The general shape: a deduplication is only as good as its definition of identity, and the button never asks you for one. Write the key yourself and the definition becomes visible, arguable and correctable.

Try it yourself

The Signups sheet is the record of what was entered and stays untouched. The Working sheet mirrors it and is where you build the comparison.

Build the normalised name, the record key, the count of rows sharing each key, and the keep-or-drop decision. Then complete the findings, including the two counts whose disagreement reveals the row you cannot see anything wrong with.

Tell real duplicates from legitimate repeats

Ten signup rows were entered by three volunteers over one week. Some rows are the same record entered twice. Some are the same person appearing legitimately more than once. One looks identical to another and is not. Build the comparison columns on the Working sheet, count what you find, and record a decision for the three rows in question. Nothing here deletes anything: the Signups sheet stays exactly as it arrived.

  1. In F2 to F11, build a normalised name for each row: the name with its spacing tidied and its case levelled, so that spelling differences stop mattering.
  2. In G2 to G11, build a record key that combines the normalised name, the date and the session, so that two rows with the same key are the same record.
  3. In H2 to H11, count how many rows share each row's key.
  4. In I2 to I11, mark each row keep or drop, so that the first row of a duplicate pair is kept and the later one is dropped.
  5. Complete the findings in J2 to J8.
  6. In L2 to L4, record your decision for each of the three rows named in column K.

This is a practice grid built for this course. It is not Excel and not Google Sheets, nothing you do here changes a file on your computer, and no spreadsheet application is involved.

Working!A1This cell is supplied; it is not editable.
Practice grid, sheet Working. Move with the arrow keys. Press Enter to edit a cell, Escape to cancel.
Row numberABCDEFGHIJKLM
1Row idName as enteredDateSessionNormalised nameRecord keyRows sharing this keyKeep or dropFindingsCleanup decisions
2S-01Ana Ruiz2026-09-01CR-101S-05: Ben Okafor, same session, same date as S-04
3S-02ana ruiz2026-09-02CR-102S-07: Cai Bell again, a different session on a different date
4S-03Ana Ruiz 2026-09-03CR-105S-08: name looks like S-01 but does not match it
5S-04Ben Okafor2026-09-01CR-101
6S-05Ben Okafor2026-09-01CR-101
7S-06Cai Bell2026-09-02CR-102
8S-07Cai Bell2026-09-04CR-106
9S-08Ana Ruiz2026-09-04CR-106
10S-09Dev Nakamura2026-09-03CR-105
11S-10Dev Nakamura2026-09-03CR-105
12
13
14
15
16

Sign in with your learning-center account to record attempts on this exercise. The grid works either way, and your work stays on this device.

Functions this grid understands

Anything else gives a #NAME? error. Arguments are separated by commas here; some regional settings in Excel and Google Sheets use semicolons instead.

  • ABS
  • AND
  • AVERAGE
  • AVERAGEIF
  • AVERAGEIFS
  • CONCAT
  • COUNT
  • COUNTA
  • COUNTBLANK
  • COUNTIF
  • COUNTIFS
  • DATE
  • DATEVALUE
  • DAY
  • EOMONTH
  • EXACT
  • FIND
  • IF
  • IFERROR
  • IFNA
  • INDEX
  • ISBLANK
  • ISERROR
  • ISNA
  • ISNUMBER
  • ISTEXT
  • LEFT
  • LEN
  • LOWER
  • MATCH
  • MAX
  • MID
  • MIN
  • MOD
  • MONTH
  • NA
  • NOT
  • OR
  • PROPER
  • RIGHT
  • ROUND
  • ROUNDDOWN
  • ROUNDUP
  • SEARCH
  • SQRT
  • SUBSTITUTE
  • SUM
  • SUMIF
  • SUMIFS
  • TEXT
  • TEXTJOIN
  • TODAY
  • TRIM
  • UPPER
  • VALUE
  • VLOOKUP
  • XLOOKUP
  • YEAR
What this practice grid does not do
  • There is no Remove Duplicates button in this grid, on purpose. Marking rows keep or drop leaves the evidence in place; a button removes it.
  • TRIM here removes ordinary spaces only, exactly as it does in Excel and Google Sheets. A non-breaking space survives it, which is why one row in this exercise refuses to match.
  • Sorting is offered with a control that lets you sort one column on its own, so you can see what that does to the rows. Use it on a copy of anything real.

Practical challenge (optional)

A supplier list holds these three entries, each with orders attached:

Ridgeline Supply
Ridgeline Supply Co.
RIDGELINE SUPPLY CO

Answer four questions on paper before touching a grid.

  1. Would a spreadsheet's ordinary equals sign treat any pair of these three as the same value? Compare each pair and explain which characters matter.
  2. TRIM and UPPER will not merge all three. Name the thing that stops them, and say why it is not a spacing problem.
  3. Suppose you merge all three into one supplier. Give one specific way that could be wrong, and one specific figure it would change.
  4. What would you need to see, outside this list, before merging them?
Compare your answers

1. None of the pairs matches. Entries two and three — Ridgeline Supply Co. and RIDGELINE SUPPLY CO — differ only in case and a full stop. Case is ignored by the equals sign, so the only difference that matters between those two is the full stop, and that is enough to make them different values. Entry one is a genuinely different string from both.

2. The differences are in the characters themselves, not in the whitespace: a full stop is present in one and absent in another, and one entry has the word "Co" that the other lacks entirely. TRIM tidies spacing and UPPER levels case; neither adds or removes a word. Merging these needs a decision about what the supplier is, which no text function can supply.

3. They might be three different companies, or a company and two of its trading divisions with separate accounts and separate payment terms. Merging them changes total spend per supplier, which is what a volume-discount threshold is measured against — so a merge could show you qualifying for a discount you do not qualify for, or hide that you are already over a single-supplier limit.

4. Something that identifies the supplier independently of its name: an account number, a tax registration, a bank detail, or an invoice from each. Two names are evidence about spelling. They are not evidence about identity.

Sign in to track your progress on this exercise.

AI collaboration

There is one job here an assistant does well and one it cannot do at all.

Useful: "What invisible characters could make two strings that look identical fail to match?" You will get a good list — non-breaking space, zero-width space, a different kind of hyphen, a right-to-left mark — and, more useful still, the formulas to detect them. That answer is checkable in ten seconds: LEN on both values, and CODE on the character in question if this grid supported it. If the lengths differ and the text looks the same, the assistant's list gave you somewhere to look.

Not useful: "Are these two suppliers the same company?" An assistant cannot know, and a plausible answer here is worse than no answer, because it is exactly the judgement that needs external evidence.

The dividing line is a good one to carry: ask an assistant about the mechanism, decide the identity yourself.

Checkpoint

Move on when you can do all of these.

  • Say what a duplicate is in terms of fields rather than in terms of names, and name the field that separates the two Cai Bell rows.
  • Say whether "ana ruiz" = "Ana Ruiz" is TRUE in a spreadsheet, and name the function to use when you need case to matter.
  • Explain what TRIM does and name one thing that looks like a space and survives it.
  • Describe the formula that marks the second and later copies of a record without marking the first, and say which part of its range is anchored.
  • Explain why a cleanup column beats deleting rows, in one sentence, to somebody in a hurry.

Sign in to track your progress on this exercise.

Summary and next step

A duplicate is a repeated record, and you decide what a record is. Build the key, count with it, mark rows rather than deleting them, and keep a visible count of the rows that look like a match and are not — because that count is the only warning you will get about the ones you cannot see.

Next: Validate, sort, filter, flag. The data is now as clean as inspection can make it. The remaining question is how to stop it getting dirty again, and how to sort and filter it without pulling records apart.

learning.goultergroup.com

The interactive parts of this page have not loaded. Reading and links still work; reload the page to try again.