helper/didyoumean: helper library for "Did you mean ...? suggestions
Uses Levenshtein distance to decide if the input is similar enough to one of the given suggestions, and returns that suggestion if so. The distance threshold of three was arrived at experimentally, and has no objective basis.
This commit is contained in:
parent
0388bc1b23
commit
ccc20fdad1
|
@ -0,0 +1,24 @@
|
||||||
|
package didyoumean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/agext/levenshtein"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NameSuggestion tries to find a name from the given slice of suggested names
|
||||||
|
// that is close to the given name and returns it if found. If no suggestion
|
||||||
|
// is close enough, returns the empty string.
|
||||||
|
//
|
||||||
|
// The suggestions are tried in order, so earlier suggestions take precedence
|
||||||
|
// if the given string is similar to two or more suggestions.
|
||||||
|
//
|
||||||
|
// This function is intended to be used with a relatively-small number of
|
||||||
|
// suggestions. It's not optimized for hundreds or thousands of them.
|
||||||
|
func NameSuggestion(given string, suggestions []string) string {
|
||||||
|
for _, suggestion := range suggestions {
|
||||||
|
dist := levenshtein.Distance(given, suggestion, nil)
|
||||||
|
if dist < 3 { // threshold determined experimentally
|
||||||
|
return suggestion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package didyoumean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNameSuggestion(t *testing.T) {
|
||||||
|
var keywords = []string{"false", "true", "null"}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
Input, Want string
|
||||||
|
}{
|
||||||
|
{"true", "true"},
|
||||||
|
{"false", "false"},
|
||||||
|
{"null", "null"},
|
||||||
|
{"bananas", ""},
|
||||||
|
{"NaN", ""},
|
||||||
|
{"Inf", ""},
|
||||||
|
{"Infinity", ""},
|
||||||
|
{"void", ""},
|
||||||
|
{"undefined", ""},
|
||||||
|
|
||||||
|
{"ture", "true"},
|
||||||
|
{"tru", "true"},
|
||||||
|
{"tre", "true"},
|
||||||
|
{"treu", "true"},
|
||||||
|
{"rtue", "true"},
|
||||||
|
|
||||||
|
{"flase", "false"},
|
||||||
|
{"fales", "false"},
|
||||||
|
{"flse", "false"},
|
||||||
|
{"fasle", "false"},
|
||||||
|
{"fasel", "false"},
|
||||||
|
{"flue", "false"},
|
||||||
|
|
||||||
|
{"nil", "null"},
|
||||||
|
{"nul", "null"},
|
||||||
|
{"unll", "null"},
|
||||||
|
{"nll", "null"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.Input, func(t *testing.T) {
|
||||||
|
got := NameSuggestion(test.Input, keywords)
|
||||||
|
if got != test.Want {
|
||||||
|
t.Errorf(
|
||||||
|
"wrong result\ninput: %q\ngot: %q\nwant: %q",
|
||||||
|
test.Input, got, test.Want,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue