fuzzysearch-1.1.3/0000755000175000017500000000000014122344530014271 5ustar sivcharisivcharifuzzysearch-1.1.3/LICENSE0000644000175000017500000000207214122344530015277 0ustar sivcharisivchariThe MIT License (MIT) Copyright (c) 2018 Peter Lithammer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fuzzysearch-1.1.3/go.mod0000644000175000017500000000012314122344530015373 0ustar sivcharisivcharimodule github.com/lithammer/fuzzysearch go 1.15 require golang.org/x/text v0.3.7 fuzzysearch-1.1.3/.github/0000755000175000017500000000000014122344530015631 5ustar sivcharisivcharifuzzysearch-1.1.3/.github/workflows/0000755000175000017500000000000014122344530017666 5ustar sivcharisivcharifuzzysearch-1.1.3/.github/workflows/go.yml0000644000175000017500000000117214122344530021017 0ustar sivcharisivchariname: Go on: [push, pull_request] jobs: test: strategy: matrix: platform: [ubuntu-latest, macos-latest, windows-latest] go-version: ['1.15', '1.16'] runs-on: ${{ matrix.platform }} steps: - name: Setup Go uses: actions/setup-go@v2.1.4 with: go-version: ${{ matrix.go-version }} - name: Checkout uses: actions/checkout@v2 - name: Download Go dependencies run: go mod download env: GOPROXY: "https://proxy.golang.org" - name: Build run: go build -v ./... - name: Test run: go test -v ./... fuzzysearch-1.1.3/.github/FUNDING.yml0000644000175000017500000000002214122344530017440 0ustar sivcharisivcharigithub: lithammer fuzzysearch-1.1.3/.github/dependabot.yml0000644000175000017500000000032214122344530020456 0ustar sivcharisivchariversion: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "monthly" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" fuzzysearch-1.1.3/README.md0000644000175000017500000000353214122344530015553 0ustar sivcharisivchari# Fuzzy Search Inspired by [bevacqua/fuzzysearch][1], a fuzzy matching library written in JavaScript. But contains some extras like ranking using [Levenshtein distance][2] and finding matches in a list of words. Fuzzy searching allows for flexibly matching a string with partial input, useful for filtering data very quickly based on lightweight user input. The current implementation uses the algorithm suggested by Mr. Aleph, a russian compiler engineer working at V8. ## Install ``` go get github.com/lithammer/fuzzysearch/fuzzy ``` ## Usage ```go package main import "github.com/lithammer/fuzzysearch/fuzzy" func main() { fuzzy.Match("twl", "cartwheel") // true fuzzy.Match("cart", "cartwheel") // true fuzzy.Match("cw", "cartwheel") // true fuzzy.Match("ee", "cartwheel") // true fuzzy.Match("art", "cartwheel") // true fuzzy.Match("eeel", "cartwheel") // false fuzzy.Match("dog", "cartwheel") // false fuzzy.Match("kitten", "sitting") // false fuzzy.RankMatch("kitten", "sitting") // -1 fuzzy.RankMatch("cart", "cartwheel") // 5 words := []string{"cartwheel", "foobar", "wheel", "baz"} fuzzy.Find("whl", words) // [cartwheel wheel] fuzzy.RankFind("whl", words) // [{whl cartwheel 6 0} {whl wheel 2 2}] // Unicode normalized matching. fuzzy.MatchNormalized("cartwheel", "cartwhéél") // true } ``` You can sort the result of a `fuzzy.RankFind()` call using the [`sort`][3] package in the standard library: ```go matches := fuzzy.RankFind("whl", words) // [{whl cartwheel 6 0} {whl wheel 2 2}] sort.Sort(matches) // [{whl wheel 2 2} {whl cartwheel 6 0}] ``` See the [`fuzzy`][4] package documentation for more examples. ## License MIT [1]: https://github.com/bevacqua/fuzzysearch [2]: http://en.wikipedia.org/wiki/Levenshtein_distance [3]: https://golang.org/pkg/sort/ [4]: https://pkg.go.dev/github.com/lithammer/fuzzysearch/fuzzy fuzzysearch-1.1.3/fuzzy/0000755000175000017500000000000014122344530015460 5ustar sivcharisivcharifuzzysearch-1.1.3/fuzzy/levenshtein.go0000644000175000017500000000202714122344530020334 0ustar sivcharisivcharipackage fuzzy // LevenshteinDistance measures the difference between two strings. // The Levenshtein distance between two words is the minimum number of // single-character edits (i.e. insertions, deletions or substitutions) // required to change one word into the other. // // This implemention is optimized to use O(min(m,n)) space and is based on the // optimized C version found here: // http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance#C func LevenshteinDistance(s, t string) int { r1, r2 := []rune(s), []rune(t) column := make([]int, len(r1)+1) for y := 1; y <= len(r1); y++ { column[y] = y } for x := 1; x <= len(r2); x++ { column[0] = x for y, lastDiag := 1, x-1; y <= len(r1); y++ { oldDiag := column[y] cost := 0 if r1[y-1] != r2[x-1] { cost = 1 } column[y] = min(column[y]+1, column[y-1]+1, lastDiag+cost) lastDiag = oldDiag } } return column[len(r1)] } func min(a, b, c int) int { if a < b && a < c { return a } else if b < c { return b } return c } fuzzysearch-1.1.3/fuzzy/levenshtein_test.go0000644000175000017500000000244214122344530021374 0ustar sivcharisivcharipackage fuzzy import "testing" var levenshteinDistanceTests = []struct { s, t string wanted int }{ {"zazz", deBelloGallico + " zazz", 1544}, {"zazz", "zazz " + deBelloGallico, 1544}, {"a", "a", 0}, {"ab", "ab", 0}, {"ab", "aa", 1}, {"ab", "aa", 1}, {"ab", "aaa", 2}, {"bbb", "a", 3}, {"kitten", "sitting", 3}, {"ёлка", "ёлочка", 2}, {"ветер", "ёлочка", 6}, {"中国", "中华人民共和国", 5}, {"日本", "中华人民共和国", 7}, } func TestLevenshtein(t *testing.T) { for _, test := range levenshteinDistanceTests { distance := LevenshteinDistance(test.s, test.t) if distance != test.wanted { t.Errorf("got distance %d, expected %d for %s in %s", distance, test.wanted, test.s, test.t) } } } func BenchmarkLevenshteinDistance(b *testing.B) { ldt := levenshteinDistanceTests[2] ldt2 := levenshteinDistanceTests[5] for i := 0; i < b.N; i++ { LevenshteinDistance(ldt.s, ldt.t) LevenshteinDistance(ldt2.s, ldt2.t) } } func BenchmarkLevenshteinDistanceBigLate(b *testing.B) { ldt := levenshteinDistanceTests[0] for i := 0; i < b.N; i++ { LevenshteinDistance(ldt.s, ldt.t) } } func BenchmarkLevenshteinDistanceBigEarly(b *testing.B) { ldt := levenshteinDistanceTests[1] for i := 0; i < b.N; i++ { LevenshteinDistance(ldt.s, ldt.t) } } fuzzysearch-1.1.3/fuzzy/fuzzy.go0000644000175000017500000001565314122344530017210 0ustar sivcharisivchari// Fuzzy searching allows for flexibly matching a string with partial input, // useful for filtering data very quickly based on lightweight user input. package fuzzy import ( "bytes" "unicode" "unicode/utf8" "golang.org/x/text/runes" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" ) func noopTransformer() transform.Transformer { return transform.Nop } func foldTransformer() transform.Transformer { return unicodeFoldTransformer{} } func normalizeTransformer() transform.Transformer { return transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC) } func normalizedFoldTransformer() transform.Transformer { return transform.Chain(normalizeTransformer(), foldTransformer()) } // Match returns true if source matches target using a fuzzy-searching // algorithm. Note that it doesn't implement Levenshtein distance (see // RankMatch instead), but rather a simplified version where there's no // approximation. The method will return true only if each character in the // source can be found in the target and occurs after the preceding matches. func Match(source, target string) bool { return match(source, target, noopTransformer()) } // MatchFold is a case-insensitive version of Match. func MatchFold(source, target string) bool { return match(source, target, foldTransformer()) } // MatchNormalized is a unicode-normalized version of Match. func MatchNormalized(source, target string) bool { return match(source, target, normalizeTransformer()) } // MatchNormalizedFold is a unicode-normalized and case-insensitive version of Match. func MatchNormalizedFold(source, target string) bool { return match(source, target, normalizedFoldTransformer()) } func match(source, target string, transformer transform.Transformer) bool { source = stringTransform(source, transformer) target = stringTransform(target, transformer) lenDiff := len(target) - len(source) if lenDiff < 0 { return false } if lenDiff == 0 && source == target { return true } Outer: for _, r1 := range source { for i, r2 := range target { if r1 == r2 { target = target[i+utf8.RuneLen(r2):] continue Outer } } return false } return true } // Find will return a list of strings in targets that fuzzy matches source. func Find(source string, targets []string) []string { return find(source, targets, noopTransformer()) } // FindFold is a case-insensitive version of Find. func FindFold(source string, targets []string) []string { return find(source, targets, foldTransformer()) } // FindNormalized is a unicode-normalized version of Find. func FindNormalized(source string, targets []string) []string { return find(source, targets, normalizeTransformer()) } // FindNormalizedFold is a unicode-normalized and case-insensitive version of Find. func FindNormalizedFold(source string, targets []string) []string { return find(source, targets, normalizedFoldTransformer()) } func find(source string, targets []string, transformer transform.Transformer) []string { var matches []string for _, target := range targets { if match(source, target, transformer) { matches = append(matches, target) } } return matches } // RankMatch is similar to Match except it will measure the Levenshtein // distance between the source and the target and return its result. If there // was no match, it will return -1. // Given the requirements of match, RankMatch only needs to perform a subset of // the Levenshtein calculation, only deletions need be considered, required // additions and substitutions would fail the match test. func RankMatch(source, target string) int { return rank(source, target, noopTransformer()) } // RankMatchFold is a case-insensitive version of RankMatch. func RankMatchFold(source, target string) int { return rank(source, target, foldTransformer()) } // RankMatchNormalized is a unicode-normalized version of RankMatch. func RankMatchNormalized(source, target string) int { return rank(source, target, normalizeTransformer()) } // RankMatchNormalizedFold is a unicode-normalized and case-insensitive version of RankMatch. func RankMatchNormalizedFold(source, target string) int { return rank(source, target, normalizedFoldTransformer()) } func rank(source, target string, transformer transform.Transformer) int { lenDiff := len(target) - len(source) if lenDiff < 0 { return -1 } source = stringTransform(source, transformer) target = stringTransform(target, transformer) if lenDiff == 0 && source == target { return 0 } runeDiff := 0 Outer: for _, r1 := range source { for i, r2 := range target { if r1 == r2 { target = target[i+utf8.RuneLen(r2):] continue Outer } else { runeDiff++ } } return -1 } // Count up remaining char runeDiff += utf8.RuneCountInString(target) return runeDiff } // RankFind is similar to Find, except it will also rank all matches using // Levenshtein distance. func RankFind(source string, targets []string) Ranks { return rankFind(source, targets, noopTransformer()) } // RankFindFold is a case-insensitive version of RankFind. func RankFindFold(source string, targets []string) Ranks { return rankFind(source, targets, foldTransformer()) } // RankFindNormalized is a unicode-normalized version of RankFind. func RankFindNormalized(source string, targets []string) Ranks { return rankFind(source, targets, normalizeTransformer()) } // RankFindNormalizedFold is a unicode-normalized and case-insensitive version of RankFind. func RankFindNormalizedFold(source string, targets []string) Ranks { return rankFind(source, targets, normalizedFoldTransformer()) } func rankFind(source string, targets []string, transformer transform.Transformer) Ranks { var r Ranks for index, target := range targets { if match(source, target, transformer) { distance := LevenshteinDistance(source, target) r = append(r, Rank{source, target, distance, index}) } } return r } type Rank struct { // Source is used as the source for matching. Source string // Target is the word matched against. Target string // Distance is the Levenshtein distance between Source and Target. Distance int // Location of Target in original list OriginalIndex int } type Ranks []Rank func (r Ranks) Len() int { return len(r) } func (r Ranks) Swap(i, j int) { r[i], r[j] = r[j], r[i] } func (r Ranks) Less(i, j int) bool { return r[i].Distance < r[j].Distance } func stringTransform(s string, t transform.Transformer) (transformed string) { var err error transformed, _, err = transform.String(t, s) if err != nil { transformed = s } return } type unicodeFoldTransformer struct{} func (unicodeFoldTransformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { runes := bytes.Runes(src) var lowerRunes []rune for _, r := range runes { lowerRunes = append(lowerRunes, unicode.ToLower(r)) } srcBytes := []byte(string(lowerRunes)) n := copy(dst, srcBytes) if n < len(srcBytes) { err = transform.ErrShortDst } return n, n, err } func (unicodeFoldTransformer) Reset() {} fuzzysearch-1.1.3/fuzzy/fuzzy_test.go0000644000175000017500000002346614122344530020250 0ustar sivcharisivcharipackage fuzzy import ( "fmt" "sort" "strings" "testing" ) const deBelloGallico = `All Gaul is divided into three parts, one of which the Belgae inhabit, the Aquitani another, those who in their own language are called Celts, in our Gauls, the third. All these differ from each other in language, customs and laws. The river Garonne separates the Gauls from the Aquitani; the Marne and the Seine separate them from the Belgae. Of all these, the Belgae are the bravest, because they are furthest from the civilization and refinement of [our] Province, and merchants least frequently resort to them, and import those things which tend to effeminate the mind; and they are the nearest to the Germans, who dwell beyond the Rhine, with whom they are continually waging war; for which reason the Helvetii also surpass the rest of the Gauls in valor, as they contend with the Germans in almost daily battles, when they either repel them from their own territories, or themselves wage war on their frontiers. One part of these, which it has been said that the Gauls occupy, takes its beginning at the river Rhone; it is bounded by the river Garonne, the ocean, and the territories of the Belgae; it borders, too, on the side of the Sequani and the Helvetii, upon the river Rhine, and stretches toward the north. The Belgae rises from the extreme frontier of Gaul, extend to the lower part of the river Rhine; and look toward the north and the rising sun. Aquitania extends from the river Garonne to the Pyrenaean mountains and to that part of the ocean which is near Spain: it looks between the setting of the sun, and the north star.` var fuzzyTests = []struct { source string target string wanted bool rank int }{ {"zazz", deBelloGallico + " zazz", true, 1544}, {"zazz", "zazz " + deBelloGallico, true, 1544}, {"twl", "cartwheel", true, 6}, {"cart", "cartwheel", true, 5}, {"cw", "cartwheel", true, 7}, {"ee", "cartwheel", true, 7}, {"art", "cartwheel", true, 6}, {"eeel", "cartwheel", false, -1}, {"dog", "cartwheel", false, -1}, {"ёлка", "ёлочка", true, 2}, {"ветер", "ёлочка", false, -1}, {"中国", "中华人民共和国", true, 5}, {"日本", "中华人民共和国", false, -1}, {"イ", "イカ", true, 1}, {"limón", "limon", false, -1}, {"kitten", "setting", false, -1}, } func TestFuzzyMatch(t *testing.T) { for _, val := range fuzzyTests { match := Match(val.source, val.target) if match != val.wanted { t.Errorf("%s in %s expected match to be %t, got %t", val.source, val.target, val.wanted, match) } } } func TestFuzzyMatchFold(t *testing.T) { for _, val := range fuzzyTests { match := MatchFold(val.source, strings.ToUpper(val.target)) if match != val.wanted { t.Errorf("%s in %s expected match to be %t, got %t", val.source, strings.ToUpper(val.target), val.wanted, match) } } } func TestFuzzyMatchNormalized(t *testing.T) { var normalizedTests = []struct { source string target string wanted bool }{ {"limon", "limón", true}, {"limón", "limon tart", true}, {"limón", "LiMóN tArT", false}, {"limón", "LeMoN tArT", false}, } for _, val := range normalizedTests { match := MatchNormalized(val.source, val.target) if match != val.wanted { t.Errorf("%s in %s expected match to be %t, got %t", val.source, val.target, val.wanted, match) } } } func TestFuzzyMatchNormalizedFold(t *testing.T) { var normalizedTests = []struct { source string target string wanted bool }{ {"limon", "limón", true}, {"limón", "limon tart", true}, {"limón", "LiMóN tArT", true}, {"limón", "LeMoN tArT", false}, } for _, val := range normalizedTests { match := MatchNormalizedFold(val.source, val.target) if match != val.wanted { t.Errorf("%s in %s expected match to be %t, got %t", val.source, val.target, val.wanted, match) } } } func TestFuzzyFind(t *testing.T) { target := []string{"cartwheel", "foobar", "wheel", "baz", "cartwhéél"} wanted := []string{"cartwheel", "wheel"} matches := Find("whel", target) if len(matches) != len(wanted) { t.Errorf("expected %s, got %s", wanted, matches) } for i := range wanted { if wanted[i] != matches[i] { t.Errorf("expected %s, got %s", wanted, matches) } } } func TestFuzzyFindNormalized(t *testing.T) { target := []string{"cartwheel", "foobar", "wheel", "baz", "cartwhéél", "WHEEL"} wanted := []string{"cartwheel", "wheel", "cartwhéél"} matches := FindNormalized("whél", target) if len(matches) != len(wanted) { t.Errorf("expected %s, got %s", wanted, matches) } for i := range wanted { if wanted[i] != matches[i] { t.Errorf("expected %s, got %s", wanted, matches) } } } func TestFuzzyFindNormalizedFold(t *testing.T) { target := []string{"cartwheel", "foobar", "wheel", "baz", "cartwhéél", "WHEEL"} wanted := []string{"cartwheel", "wheel", "cartwhéél", "WHEEL"} matches := FindNormalizedFold("whél", target) if len(matches) != len(wanted) { t.Errorf("expected %s, got %s", wanted, matches) } for i := range wanted { if wanted[i] != matches[i] { t.Errorf("expected %s, got %s", wanted, matches) } } } func TestRankMatch(t *testing.T) { for _, val := range fuzzyTests { rank := RankMatch(val.source, val.target) if rank != val.rank { t.Errorf("expected ranking %d, got %d for %s in %s", val.rank, rank, val.source, val.target) } } } func TestRankMatchNormalized(t *testing.T) { var fuzzyTests = []struct { source string target string rank int }{ {"limó", "limon", 1}, {"limó", "limon", 1}, {"limó", "LIMON", -1}, } for _, val := range fuzzyTests { rank := RankMatchNormalized(val.source, val.target) if rank != val.rank { t.Errorf("expected ranking %d, got %d for %s in %s", val.rank, rank, val.source, val.target) } } } func TestRankMatchNormalizedFold(t *testing.T) { var fuzzyTests = []struct { source string target string rank int }{ {"limó", "limon", 1}, {"limó", "limon", 1}, {"limó", "LIMON", 1}, {"limó", "LIMON TART", 6}, } for _, val := range fuzzyTests { rank := RankMatchNormalizedFold(val.source, val.target) if rank != val.rank { t.Errorf("expected ranking %d, got %d for %s in %s", val.rank, rank, val.source, val.target) } } } func TestRankMatchNormalizedFoldConcurrent(t *testing.T) { target := strings.Split("Lorem ipsum dolor sit amet, consectetur adipiscing elit", " ") source := "ips" procs := 10 iter := 10 type empty struct{} done := make(chan empty) for i := 0; i <= procs; i++ { go func() { for n := 0; n < iter; n++ { _ = RankFindNormalizedFold(source, target) } done <- empty{} }() } cnt := 0 for i := 0; i < procs; i++ { <-done cnt++ } } func TestRankFind(t *testing.T) { target := []string{"cartwheel", "foobar", "wheel", "baz"} wanted := []Rank{ {"whl", "cartwheel", 6, 0}, {"whl", "wheel", 2, 2}, } ranks := RankFind("whl", target) if len(ranks) != len(wanted) { t.Errorf("expected %+v, got %+v", wanted, ranks) } for i := range wanted { if wanted[i] != ranks[i] { t.Errorf("expected %+v, got %+v", wanted, ranks) } } } func TestRankFindNormalized(t *testing.T) { target := []string{"limón", "limon", "lemon", "LIMON"} wanted := []Rank{ {"limó", "limón", 1, 0}, {"limó", "limon", 2, 1}, } ranks := RankFindNormalized("limó", target) if len(ranks) != len(wanted) { t.Errorf("expected %+v, got %+v", wanted, ranks) } for i := range wanted { if wanted[i] != ranks[i] { t.Errorf("expected %+v, got %+v", wanted, ranks) } } } func TestRankFindNormalizedFold(t *testing.T) { target := []string{"limón", "limon", "lemon", "LIMON"} wanted := []Rank{ {"limó", "limón", 1, 0}, {"limó", "limon", 2, 1}, {"limó", "LIMON", 5, 3}, } ranks := RankFindNormalizedFold("limó", target) if len(ranks) != len(wanted) { t.Errorf("expected %+v, got %+v", wanted, ranks) } for i := range wanted { if wanted[i] != ranks[i] { t.Errorf("expected %+v, got %+v", wanted, ranks) } } } func TestSortingRanks(t *testing.T) { rs := Ranks{{"a", "b", 1, 0}, {"a", "cc", 2, 1}, {"a", "a", 0, 2}} wanted := Ranks{rs[2], rs[0], rs[1]} sort.Sort(rs) for i := range wanted { if wanted[i] != rs[i] { t.Errorf("expected %+v, got %+v", wanted, rs) } } } func BenchmarkMatch(b *testing.B) { ft := fuzzyTests[2] for i := 0; i < b.N; i++ { Match(ft.source, ft.target) } } func BenchmarkMatchBigLate(b *testing.B) { ft := fuzzyTests[0] for i := 0; i < b.N; i++ { Match(ft.source, ft.target) } } func BenchmarkMatchBigEarly(b *testing.B) { ft := fuzzyTests[1] for i := 0; i < b.N; i++ { Match(ft.source, ft.target) } } func BenchmarkMatchFold(b *testing.B) { ft := fuzzyTests[2] for i := 0; i < b.N; i++ { MatchFold(ft.source, ft.target) } } func BenchmarkMatchFoldBigLate(b *testing.B) { ft := fuzzyTests[0] for i := 0; i < b.N; i++ { MatchFold(ft.source, ft.target) } } func BenchmarkMatchFoldBigEarly(b *testing.B) { ft := fuzzyTests[1] for i := 0; i < b.N; i++ { MatchFold(ft.source, ft.target) } } func BenchmarkRankMatch(b *testing.B) { ft := fuzzyTests[2] for i := 0; i < b.N; i++ { RankMatch(ft.source, ft.target) } } func BenchmarkRankMatchBigLate(b *testing.B) { ft := fuzzyTests[0] for i := 0; i < b.N; i++ { RankMatch(ft.source, ft.target) } } func BenchmarkRankMatchBigEarly(b *testing.B) { ft := fuzzyTests[1] for i := 0; i < b.N; i++ { RankMatch(ft.source, ft.target) } } func ExampleMatch() { fmt.Print(Match("twl", "cartwheel")) // Output: true } func ExampleFind() { fmt.Print(Find("whl", []string{"cartwheel", "foobar", "wheel", "baz"})) // Output: [cartwheel wheel] } func ExampleRankMatch() { fmt.Print(RankMatch("twl", "cartwheel")) // Output: 6 } func ExampleRankFind() { fmt.Printf("%+v", RankFind("whl", []string{"cartwheel", "foobar", "wheel", "baz"})) // Output: [{Source:whl Target:cartwheel Distance:6 OriginalIndex:0} {Source:whl Target:wheel Distance:2 OriginalIndex:2}] } fuzzysearch-1.1.3/go.sum0000644000175000017500000000040614122344530015424 0ustar sivcharisivcharigolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=