Soundex is an algorithm for creating indices for words based on their pronunciation.



Task

The goal is for homophones to be encoded to the same representation so that they can be matched despite minor differences in spelling   (from the   soundex   Wikipedia article).

Caution

There is a major issue in many of the implementations concerning the separation of two consonants that have the same soundex code! According to the official Rules [[1]]. So check for instance if Ashcraft is coded to A-261.

 If a vowel (A, E, I, O, U) separates two consonants that have the same soundex code, the consonant to the right of the vowel is coded. Tymczak is coded as T-522 (T, 5 for the M, 2 for the C, Z ignored (see "Side-by-Side" rule above), 2 for the K). Since the vowel "A" separates the Z and K, the K is coded.
 If "H" or "W" separate two consonants that have the same soundex code, the consonant to the right of the vowel is not coded. Example: Ashcraft is coded A-261 (A, 2 for the S, C ignored, 6 for the R, 1 for the F). It is not coded A-226.


defmodule Soundex do
  def soundex([]), do: []
  def soundex(str) do
    [head|tail] = String.upcase(str) |> to_char_list
    [head | isoundex(tail, [], todigit(head))]
  end
 
  defp isoundex([], acc, _) do
    case length(acc) do
      n when n == 3 -> Enum.reverse(acc)
      n when n <  3 -> isoundex([], [?0 | acc], :ignore)
      n when n >  3 -> isoundex([], Enum.slice(acc, n-3, n), :ignore)
    end
  end
  defp isoundex([head|tail], acc, lastn) do
    dig = todigit(head)
    if dig != ?0 and dig != lastn do
      isoundex(tail, [dig | acc], dig)
    else
      case head do
        ?H                 -> isoundex(tail, acc, lastn)
        ?W                 -> isoundex(tail, acc, lastn)
        n when n in ?A..?Z -> isoundex(tail, acc, dig)
        _                  -> isoundex(tail, acc, lastn)  # This clause handles non alpha characters
      end
    end
  end
 
  @digits  '01230120022455012623010202'
  defp todigit(chr) do    
    if chr in ?A..?Z, do: Enum.at(@digits, chr - ?A),
                    else: ?0            # Treat non alpha characters as a vowel
  end
end
 
IO.puts Soundex.soundex("Soundex")
IO.puts Soundex.soundex("Example")
IO.puts Soundex.soundex("Sownteks")
IO.puts Soundex.soundex("Ekzampul")