Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Let's Trie Something

examples/trie.livemd

Let’s Trie Something

Trie using maps and atoms

defmodule Trie do
  def new() do
    %{}
  end

  def atomize(word) do
    word
    |> String.graphemes()
    |> Enum.map(&String.to_atom/1)
  end

  def variants(:e), do: [:e, :"3"]
  def variants(:o), do: [:o, :"0"]
  def variants(:l), do: [:l, :"1", :|]
  def variants(any), do: [any]

  def insert(trie, word) when is_binary(word) do
    insert(trie, atomize(word))
  end

  def insert(trie, [next | rest]) do
    variants(next)
    |> Enum.reduce(trie, fn letter, acc ->
      case Map.has_key?(acc, letter) do
        true -> Map.put(acc, letter, insert(trie[letter], rest))
        false -> Map.put(acc, letter, insert(Trie.new(), rest))
      end
    end)
  end

  def insert(trie, []) do
    Map.put(trie, :stop, 1)
  end

  def contains?(trie, word) when is_binary(word) do
    contains?(trie, atomize(word))
  end

  def contains?(trie, [next | rest]) do
    case Map.has_key?(trie, next) do
      true -> contains?(trie[next], rest)
      false -> false
    end
  end

  def contains?(%{:stop => _frequency}, []) do
    true
  end

  def contains?(_trie, []) do
    false
  end
end
trie = Trie.new()
trie = Trie.insert(trie, "hello")
trie = Trie.insert(trie, "hi")
{
  "hello",
  Trie.contains?(trie, "hello"),
  "h3llo",
  Trie.contains?(trie, "h3llo"),
  "he11o",
  Trie.contains?(trie, "he11o"),
  "h3110",
  Trie.contains?(trie, "h3110")
}
Trie.contains?(trie, "hell")
Trie.contains?(trie, "hi")
trie = Trie.new()

trie =
  File.stream!("/usr/share/dict/words")
  |> Stream.map(&String.trim/1)
  |> Enum.reduce(trie, fn word, acc ->
    Trie.insert(acc, word)
  end)
[
  Trie.contains?(trie, "loafer"),
  Trie.contains?(trie, "l0afer"),
  Trie.contains?(trie, "10afer"),
  Trie.contains?(trie, "l0af3r")
]