From e648f269ea752bb412b3239ebc1c38fe2bffaea7 Mon Sep 17 00:00:00 2001 From: Jean Parpaillon Date: Fri, 8 Sep 2017 16:04:35 +0200 Subject: [PATCH 1/4] Add function 'prefix!/2': returns matching bianries like 'prefix/2', along with longest common prefix of matching binaries. Very common when implementing auto-completion. --- lib/retrieval.ex | 38 ++++++++++++++++++++++++++++++++++++++ test/retrieval_test.exs | 9 ++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/retrieval.ex b/lib/retrieval.ex index 2af4e91..003c89c 100644 --- a/lib/retrieval.ex +++ b/lib/retrieval.ex @@ -132,6 +132,44 @@ defmodule Retrieval do end) end + @doc """ + Collects all binaries that begin with a given prefix. Returns matching binaries, along + with matching binaries' longest common prefix. + + ## Examples + + Retrieval.new(~w/apple apply ape/) |> Retrieval.prefix!("a") + {"ap", ["apple", "apply", "ape"]} + + Retrieval.new(~w/apple apply ape ample/) |> Retrieval.prefix("z") + {nil, []} + + """ + + def prefix!(%Trie{trie: trie}, binary) when is_binary(binary) do + _prefix!(trie, binary, binary) + end + + defp _prefix!(trie, <>, acc) do + case Map.has_key?(trie, next) do + true -> _prefix!(trie[next], rest, acc) + false -> {nil, []} + end + end + + defp _prefix!(trie, <<>>, acc) do + case Enum.count(trie) do + 1 -> + case Map.keys(trie) do + [:mark] -> {acc, [acc]} + [ch] -> _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 c9971a9..b7974fe 100644 --- a/test/retrieval_test.exs +++ b/test/retrieval_test.exs @@ -4,7 +4,8 @@ defmodule RetrievalTest 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 + dad daddy dadoobidoo/ @test_trie Retrieval.new(@test_data) @@ -25,6 +26,12 @@ defmodule RetrievalTest do assert Retrieval.prefix(@test_trie, "abc") == [] end + test "prefix!" do + assert Retrieval.prefix!(@test_trie, "da") == {"dad", ["daddy", "dadoobidoo", "dad"]} + assert Retrieval.prefix!(@test_trie, "winter") == {"winter", ["winter"]} + assert Retrieval.prefix!(@test_trie, "abc") == {nil, []} + end + test "pattern errors" do assert match?({:error, _}, Retrieval.pattern(@test_trie, "ab*[^zsd")) assert match?({:error, _}, Retrieval.pattern(@test_trie, "ab*[^zsd]{}")) From 0ab0744cbba1bb9f2e623029a78ad5ab481f2ab2 Mon Sep 17 00:00:00 2001 From: Jean Parpaillon Date: Fri, 22 Sep 2017 11:01:34 +0200 Subject: [PATCH 2/4] Fix warnings with recent elixir --- mix.exs | 10 +++++----- mix.lock | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index bd39d8b..7d2abe2 100644 --- a/mix.exs +++ b/mix.exs @@ -5,11 +5,11 @@ defmodule Retrieval.Mixfile do [app: :retrieval, version: "0.9.1", elixir: "~> 1.2", - description: description, - package: package, + description: description(), + package: package(), build_embedded: Mix.env == :prod, start_permanent: Mix.env == :prod, - deps: deps] + deps: deps()] end def application do @@ -17,8 +17,8 @@ defmodule Retrieval.Mixfile do end defp deps do - [{:earmark, "~> 0.1", only: :dev}, - {:ex_doc, "~> 0.11", only: :dev}] + [{:earmark, "~> 1.2", only: :dev}, + {:ex_doc, "~> 0.16", only: :dev}] end def description do diff --git a/mix.lock b/mix.lock index b7b9e25..47e155f 100644 --- a/mix.lock +++ b/mix.lock @@ -1,2 +1,2 @@ -%{"earmark": {:hex, :earmark, "0.2.1"}, - "ex_doc": {:hex, :ex_doc, "0.11.4"}} +%{"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.16.4", "4bf6b82d4f0a643b500366ed7134896e8cccdbab4d1a7a35524951b25b1ec9f0", [], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}} From 7b6e537206c9c5f4987fd456c8256a53120b743e Mon Sep 17 00:00:00 2001 From: Jean Parpaillon Date: Fri, 22 Sep 2017 11:02:11 +0200 Subject: [PATCH 3/4] Ignore build doc in git --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 755b605..008b35d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /_build /cover /deps +/doc erl_crash.dump *.ez From bbd3f8bab8367aa8cd814f64946de8f1ba9de982 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 28 Dec 2024 00:05:57 -0500 Subject: [PATCH 4/4] rename prefix!() to longest_common_prefix() and update test --- lib/retrieval.ex | 30 +++++++++++++++--------------- test/retrieval_test.exs | 11 ++++++----- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/retrieval.ex b/lib/retrieval.ex index b2e8bba..2cda11f 100644 --- a/lib/retrieval.ex +++ b/lib/retrieval.ex @@ -134,39 +134,39 @@ defmodule Trieval do @doc """ Collects all binaries that begin with a given prefix. Returns matching binaries, along - with matching binaries' longest common prefix. + with matching binaries' longest common prefix. Example use-case would be for auto-completion. ## Examples - Retrieval.new(~w/apple apply ape/) |> Retrieval.prefix!("a") + Trieval.new(~w/apple apply ape/) |> Trieval.longest_common_prefix("a") {"ap", ["apple", "apply", "ape"]} - Retrieval.new(~w/apple apply ape ample/) |> Retrieval.prefix("z") + Trieval.new(~w/apple apply ape ample/) |> Trieval.longest_common_prefix("z") {nil, []} """ - - def prefix!(%Trie{trie: trie}, binary) when is_binary(binary) do - _prefix!(trie, binary, binary) + + def longest_common_prefix(%Trie{trie: trie}, binary) when is_binary(binary) do + _longest_common_prefix(trie, binary, binary) end - defp _prefix!(trie, <>, acc) do + defp _longest_common_prefix(trie, <>, acc) do case Map.has_key?(trie, next) do - true -> _prefix!(trie[next], rest, acc) + true -> _longest_common_prefix(trie[next], rest, acc) false -> {nil, []} end end - defp _prefix!(trie, <<>>, acc) do + defp _longest_common_prefix(trie, <<>>, acc) do case Enum.count(trie) do 1 -> - case Map.keys(trie) do - [:mark] -> {acc, [acc]} - [ch] -> _prefix!(trie[ch], <<>>, acc <> <>) - end + case Map.keys(trie) do + [:mark] -> {acc, [acc]} + [ch] -> _longest_common_prefix(trie[ch], <<>>, acc <> <>) + end _ -> - matches = _prefix(trie, <<>>, acc) - {acc, matches} + matches = _prefix(trie, <<>>, acc) + {acc, matches} end end diff --git a/test/retrieval_test.exs b/test/retrieval_test.exs index 03250c9..fee06ce 100644 --- a/test/retrieval_test.exs +++ b/test/retrieval_test.exs @@ -5,7 +5,7 @@ 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 - dad daddy dadoobidoo/ + friend friendly fried frieze/ @test_trie Trieval.new(@test_data) @@ -26,10 +26,11 @@ defmodule TrievalTest do assert Trieval.prefix(@test_trie, "abc") == [] end - test "prefix!" do - assert Trieval.prefix!(@test_trie, "da") == {"dad", ["daddy", "dadoobidoo", "dad"]} - assert Trieval.prefix!(@test_trie, "winter") == {"winter", ["winter"]} - assert Trieval.prefix!(@test_trie, "abc") == {nil, []} + 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