Current section
Files
Jump to
Current section
Files
src/jaro.erl~
-module(jaro).
%% From Erlang string module r27
-export([similarity/2, debug/2, str_to_indexmap/1]).
debug(A0, B0) ->
{A, ALen} = str_to_gcl_and_length(A0),
{B, BLen} = str_to_indexmap(B0),
{{A, ALen} , {B, BLen}}.
-spec similarity(String1, String2) -> Similarity when
String1 :: unicode:chardata(),
String2 :: unicode:chardata(),
Similarity :: float(). %% Between +0.0 and 1.0
similarity(A0, B0) ->
% io:format("~s | ~s~n", [A0, B0]),
{A, ALen} = str_to_gcl_and_length(A0),
{B, BLen} = str_to_indexmap(B0),
Dist = max(ALen, BLen) div 2,
{AM, BM} = jaro_match(A, B, -Dist, Dist, [], []),
if
ALen =:= 0 andalso BLen =:= 0 ->
1.0;
ALen =:= 0 orelse BLen =:= 0 ->
0.0;
AM =:= [] ->
0.0;
true ->
{M,T} = jaro_calc_mt(AM, BM, 0, 0),
(M/ALen + M/BLen + (M-T/2)/M) / 3
end.
jaro_match([A|As], B0, Min, Max, AM, BM) ->
case jaro_detect(maps:get(A, B0, []), Min, Max) of
false ->
jaro_match(As, B0, Min+1, Max+1, AM, BM);
{J, Remain} ->
B = B0#{A => Remain},
jaro_match(As, B, Min+1, Max+1, [A|AM], add_rsorted({J,A},BM))
end;
jaro_match(_A, _B, _Min, _Max, AM, BM) ->
{AM, BM}.
jaro_detect([Idx|Rest], Min, Max) when Min < Idx, Idx < Max ->
{Idx, Rest};
jaro_detect([Idx|Rest], Min, Max) when Idx < Max ->
jaro_detect(Rest, Min, Max);
jaro_detect(_, _, _) ->
false.
jaro_calc_mt([CharA|AM], [{_, CharA}|BM], M, T) ->
jaro_calc_mt(AM, BM, M+1, T);
jaro_calc_mt([_|AM], [_|BM], M, T) ->
jaro_calc_mt(AM, BM, M+1, T+1);
jaro_calc_mt([], [], M, T) ->
{M, T}.
%% Returns GC list and length
str_to_gcl_and_length(S0) ->
gcl_and_length(unicode_util:gc(S0), [], 0).
gcl_and_length([C|Str], Acc, N) ->
gcl_and_length(unicode_util:gc(Str), [C|Acc], N+1);
gcl_and_length([], Acc, N) ->
{lists:reverse(Acc), N};
gcl_and_length({error, Err}, _, _) ->
error({badarg, Err}).
%% Returns GC map with index and length
str_to_indexmap(S) ->
[M|L] = str_to_map(unicode_util:gc(S), 0),
{M,L}.
str_to_map([], L) -> [#{}|L];
str_to_map([G | Gs], I) ->
[M|L] = str_to_map(unicode_util:gc(Gs), I+1),
[maps:put(G, [I | maps:get(G, M, [])], M)| L];
str_to_map({error,Error}, _) ->
io:format("#2 ~p~n", [Error]),
error({badarg, Error}).
%% Add in decreasing order
add_rsorted(A, [H|_]=BM) when A > H ->
[A|BM];
add_rsorted(A, [H|BM]) ->
[H|add_rsorted(A,BM)];
add_rsorted(A, []) ->
[A].