csvkit.org
CSV (Comma-Separated Values) utilities, in the browser
Say hi →

Fuzzy Duplicate Finder for CSV

updated 31 August 2026

match
ignore
Drop a .csv file here, or
ready

Fuzzy Duplicate Finder

Exact dedupe only catches rows that already agree. Real duplicate records disagree: Ada Lovelace, ada  lovelace, Ada Lovelace. and Ada Lovelcae are one person and four rows, and a plain dedupe keeps all four. This normalises hard, then measures what is left — and shows you the groups with a score before it deletes anything.

How to use it

  1. Paste or drop the CSV. The column names appear under the panes — click the ones that identify a record (usually a name plus an email, or a company plus a postcode). Pick none and every column is used, which is stricter than it sounds.
  2. Leave Do on show the groups for the first run. You get only the rows involved in a near-match, grouped, each with its similarity to the row that would survive.
  3. Move the threshold until the groups look right. 88% is a good start; lower catches more and starts to catch coincidences.
  4. Switch to delete the duplicates when you trust it, and download.

What "similar" means here

Two steps, and the first does most of the work.

1. Normalise

The chosen columns are joined into one key, and then — depending on the ignore toggles — lower-cased, stripped of punctuation, collapsed to single spaces, and decomposed to remove accents (José becomes jose, not jos). Everything but digits is the one that is off by default: it throws away all non-digits, which is exactly right for phone numbers, account IDs and postcodes written six different ways, and useless for names.

2. Measure

What survives normalisation is compared with an optimal string alignment distance — Levenshtein, plus a move for swapping two adjacent characters. That extra move matters more than it sounds: transposition is the commonest typo there is, and plain Levenshtein charges LovelaceLovelcae as two edits, scoring 83% on a 12-character key and falling under any sensible threshold. Counted as one edit it scores 92% and matches.

The score is 1 − distance / length of the longer string, so it is a percentage of the key that had to change.

How it stays fast

Comparing every row with every other row is quadratic: 20,000 rows is 200 million comparisons and a hung tab. Two things prevent that.

Grouping is by union-find, so a chain of matches ends up in one group: if A matches B and B matches C, all three are one record even when A and C are not similar to each other on their own. The comparison count is reported in the status bar. Input is capped at 20,000 rows.

The three modes

Which row survives

Example

name,email,city
Ada Lovelace,[email protected],London
ada  lovelace,[email protected],london
Ada Lovelcae,[email protected],
Grace Hopper,[email protected],New York

At 88%, matching on name, in show the groups mode:

group,role,similarity,name,email,city
1,keep,100.0%,Ada Lovelace,[email protected],London
1,duplicate,100.0%,ada  lovelace,[email protected],london
1,duplicate,91.7%,Ada Lovelcae,[email protected],

The second row scores 100% because casing and double spacing are removed before anything is measured. The third is one transposition away. Grace Hopper is not in a group and does not appear.

Limits and gotchas

Privacy

100% client-side. The file is parsed and compared in your browser; nothing is uploaded. See the privacy policy.

Related tools