Cairn
algorithms · approximate match · O(n)

back to Approximate Match

Soundex

The site's seventh approximate match entry, and the first that isn't measuring anything. Damerau–Levenshtein, Banded Edit Distance, Bitap with Edit Distance, and Bitap all count edits; Jaro–Winkler computes a graded similarity score. Soundex does neither — it maps every string to a fixed-width, 4-character code, and two strings either land on the identical code or they don't. There's no "close." It was patented in 1918 by Robert C. Russell (refined through the 1930s), built to let U.S. Census clerks file immigration-era surname misspellings — Katherine/Catherine, Smith/Smyth — under one shared bucket regardless of exactly how each was spelled that year. It's still shipped today as a built-in SOUNDEX() function in MySQL, PostgreSQL, and SQL Server.

Try it

Pick a pair and press Step or Run to watch both names get encoded, letter by letter, then compare the two finished codes. Robert/Rupert and Ashcraft/Ashcroft are genuine matches — different spellings, same code. Bard/ Board is also a match, and a pitfall (see below): five unrelated words share this one code. Knight/Night is the opposite kind of surprise: true homophones that Soundex reports as no match at all. The checkbox reproduces a real implementation bug — see the third pitfall.

name A
name B
step 0
Press Step or Run.

Why it works

Every letter belongs to exactly one of three groups:

DigitLetters
1B, F, P, V
2C, G, J, K, Q, S, X, Z
3D, T
4L
5M, N
6R
(uncoded)A, E, I, O, U, Y — vowels — and H, W

The digit groups aren't arbitrary — each one clusters consonants that sound alike or are commonly confused in handwriting and speech (B/F/P/V are all made with the lips; C/G/J/K/Q/S/X/Z all sit near the back of the mouth or hiss). Building the code:

1. Keep the first letter literally — never converted to a digit, whatever group it belongs to.

2. Walk the rest of the string, converting each consonant to its digit. Skip vowels (and Y) entirely, and skip H and W entirely too — but with one crucial difference. A vowel resets a piece of merge memory (see step 3); H and W do not.

3. Merge adjacent duplicates. If a letter's digit is the same as the last digit actually written, drop it instead of writing it again — Tymczak's C and Z are both digit 2 and adjacent, so only one 2 is written (T522, not T5222). Because H and W don't reset the merge memory, two same-digit consonants separated only by a silent H or W still merge: Ashcraft's S and C are both digit 2, with only an H between them, so they merge into one 2 too (A261, not A226) — see the third pitfall for what breaks when an implementation gets this rule backwards.

4. Pad or truncate to exactly 4 characters — the kept first letter plus 3 digits. Short inputs (LeeL000) are padded with zeros; longer ones are cut off after the third digit, however many consonants remain unconverted.

Reference implementation

function soundex(str) {
  const codeMap = {
    b:'1', f:'1', p:'1', v:'1',
    c:'2', g:'2', j:'2', k:'2', q:'2', s:'2', x:'2', z:'2',
    d:'3', t:'3',
    l:'4',
    m:'5', n:'5',
    r:'6'
  };
  const letters = str.toUpperCase().replace(/[^A-Z]/g, '');
  if (!letters) return '';

  let result = letters[0];
  let lastCode = null;
  for (let i = 1; i < letters.length; i++) {
    const ch = letters[i].toLowerCase();
    if (ch === 'h' || ch === 'w') continue;      // skip, but preserve lastCode
    const code = codeMap[ch] || null;
    if (code === null) { lastCode = null; continue; } // vowel or Y: skip, reset
    if (code !== lastCode) result += code;
    lastCode = code;
  }
  return (result + '000').slice(0, 4);
}

Checked against eight of the standard reference examples used across Soundex implementations and the U.S. National Archives' own documentation, before writing any of the prose above: Robert and Rupert both R163, Ashcraft and Ashcroft both A261, RubinR150, TymczakT522 (not T5222), PfisterP123 (not P1226 — the first letter's own group never merges with the very next letter, even when they share a digit), and HoneymanH555. All eight matched exactly.

Pitfalls

The digit groups are coarse enough that entirely unrelated words collide. A sweep of common English names and words found Bard, Board, Beard, Bird, and Byrd — five words that don't mean or sound alike beyond sharing a leading B and an R somewhere — all encode to B630. Try them in the demo above. This is the flip side of the design goal: a scheme loose enough to catch Smith/Smyth is also loose enough to catch things that were never supposed to match, and a genealogical or database lookup built on Soundex alone will surface irrelevant results for exactly this reason.

The first letter is never converted, which creates a blind spot for real homophones. Knight and Night are pronounced identically, but Soundex encodes them as K523 and N230 — no shared digits at all beyond both starting with a letter. Because the first letter is always kept literally rather than folded into the same digit groups as everything after it, a silent leading consonant (here, the silent K) guarantees a completely different code family, no matter how identical the rest of the pronunciation is. Later phonetic schemes (Metaphone, NYSIIS) were built in part to close gaps like this and the collision above, at the cost of a more complex rule set than Soundex's simple digit lookup.

Getting the H/W merge-preservation rule backwards breaks a textbook example. The demo's checkbox swaps in a variant that treats H and W exactly like vowels — resetting the merge memory instead of preserving it. Run it on Ashcraft: the correct rule merges the S and C across the silent H into one digit, giving A261; the broken rule lets the H reset the merge memory, so both letters get written, giving A226 instead — silently wrong, not crashed, and exactly the "not A226" case the U.S. National Archives' own documentation calls out by name.

Complexity

Time: O(n) — one pass over the input, one constant-time table lookup per letter. Space: O(1) beyond the fixed 4-character output, regardless of input length — no table, no window, nothing proportional to the string at all. That's asymptotically cheaper than every edit-distance-based sibling on this page, which is exactly what you'd expect from an algorithm that throws away almost all of the string's information on purpose, in exchange for an equality check instead of a graded comparison.

Soundex answers a different question than every other approximate match entry on this site: not "how many edits apart" or "how similar," but "would a person filing these by ear put them in the same drawer?" For an approach that measures actual spelling similarity instead, see Damerau–Levenshtein Distance or Jaro–Winkler Similarity. For a side-by-side comparison across all eleven approximate-match entries, organized by which problem you actually have, see Choosing an Approximate String Matcher.