Packages

A simple library for searching through a wordlist for words matching certain patterns.

Current section

Files

Jump to
word_finder lib word_finder.ex
Raw

lib/word_finder.ex

defmodule WordFinder do
@moduledoc """
Provides a way of looking up words knowing only partials of it. For example, I want to know all words which fit the following pattern "bana__", then I'd get back something like ["banana", ...] (other words with the same length, but different __). Results depend on what dictionary you are currently using. The only supported locale at this moment is sv_SE.
## Examples
iex> WordFinder.search(model, "bana__"
["banana"]
"""
@spec train(string, string) :: Map.t
def train(source, destination) do
stream = File.stream! source, [:utf8]
structure = Enum.reduce(stream,
%{},
fn(x, table) -> process_word(x, table) end)
File.write! destination, :erlang.term_to_binary(structure)
structure
end
@spec model(string) :: Map.t
def model(source) do
source |> File.read!
|> :erlang.binary_to_term
end
@spec search(Map.t, string) :: [String.t]
def search(register, query) do
split = String.split(query, "")
first_known_index = Enum.find_index(split, fn(i) -> i != "." end)
first_known = Enum.at(split, first_known_index)
word_list = register[first_known][String.length(query)][first_known_index]
matches = Enum.filter(word_list, fn(x) -> matches?(query, x) end)
matches
end
defp matches?(query, target) do
[String.split(query, "", trim: true),
String.split(target, "", trim: true)]
|> List.zip
|> Enum.all?(fn(x) -> elem(x, 0) == "." || elem(x, 0) == elem(x, 1) end)
end
defp process_word(line, table) do
word = line
|> String.trim
|> String.downcase
|> String.split("", trim: true)
word_stream = Stream.with_index word
Enum.reduce(word_stream,
table,
fn(p, alphabetic_table) -> process_letter(p, word, alphabetic_table) end)
end
defp process_letter(pair, word, alphabetic_table) do
letter = elem(pair, 0)
index = elem(pair, 1)
initial = add_to_length_map(%{}, index, word)
Map.update(alphabetic_table, letter, initial,
fn(l) -> add_to_length_map(l, index, word) end)
end
defp add_to_length_map(length_vector, index, word1) do
word = Enum.join(word1, "")
initial = add_to_position_map(%{}, index, word)
Map.update(length_vector,
String.length(word),
initial,
fn(v) -> add_to_position_map(v, index, word) end)
end
defp add_to_position_map(position_map, index, word) do
Map.update(position_map, index, [word], fn(v) -> v ++ [word] end)
end
end