diff --git a/lib/retrieval.ex b/lib/retrieval.ex index ad7ce4a..2cda11f 100644 --- a/lib/retrieval.ex +++ b/lib/retrieval.ex @@ -132,6 +132,44 @@ defmodule Trieval do end) end + @doc """ + Collects all binaries that begin with a given prefix. Returns matching binaries, along + with matching binaries' longest common prefix. Example use-case would be for auto-completion. + + ## Examples + + Trieval.new(~w/apple apply ape/) |> Trieval.longest_common_prefix("a") + {"ap", ["apple", "apply", "ape"]} + + Trieval.new(~w/apple apply ape ample/) |> Trieval.longest_common_prefix("z") + {nil, []} + + """ + + def longest_common_prefix(%Trie{trie: trie}, binary) when is_binary(binary) do + _longest_common_prefix(trie, binary, binary) + end + + defp _longest_common_prefix(trie, <>, acc) do + case Map.has_key?(trie, next) do + true -> _longest_common_prefix(trie[next], rest, acc) + false -> {nil, []} + end + end + + defp _longest_common_prefix(trie, <<>>, acc) do + case Enum.count(trie) do + 1 -> + case Map.keys(trie) do + [:mark] -> {acc, [acc]} + [ch] -> _longest_common_prefix(trie[ch], <<>>, acc <> <>) + end + _ -> + matches = _prefix(trie, <<>>, acc) + {acc, matches} + end + end + @doc """ Collects all binaries match a given pattern. Returns either a list of matches or an error in the form `{:error, reason}`. diff --git a/test/retrieval_test.exs b/test/retrieval_test.exs index 29b3e0d..fee06ce 100644 --- a/test/retrieval_test.exs +++ b/test/retrieval_test.exs @@ -4,7 +4,8 @@ defmodule TrievalTest do @test_data ~w/apple apply ape bed between betray cat cold hot warm winter maze smash crush under above people - negative poison place out divide zebra extended/ + negative poison place out divide zebra extended + friend friendly fried frieze/ @test_trie Trieval.new(@test_data) @@ -25,6 +26,13 @@ defmodule TrievalTest do assert Trieval.prefix(@test_trie, "abc") == [] end + test "longest_common_prefix" do + assert Trieval.longest_common_prefix(@test_trie, "fr") == {"frie", ["fried", "friendly", "friend", "frieze"]} + assert Trieval.longest_common_prefix(@test_trie, "frien") == {"friend", ["friendly", "friend"]} + assert Trieval.longest_common_prefix(@test_trie, "winter") == {"winter", ["winter"]} + assert Trieval.longest_common_prefix(@test_trie, "abc") == {nil, []} + end + test "pattern errors" do assert match?({:error, _}, Trieval.pattern(@test_trie, "ab*[^zsd")) assert match?({:error, _}, Trieval.pattern(@test_trie, "ab*[^zsd]{}"))