### Install Fuzzy Search Go Package Source: https://github.com/lithammer/fuzzysearch/blob/master/README.md Use 'go get' to install the fuzzy search library. ```bash go get github.com/lithammer/fuzzysearch/fuzzy ``` -------------------------------- ### Example: Using RankFind to Get Ranked Matches Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/ranks.md Demonstrates how to use the RankFind function to find fuzzy matches and print the details of each ranked result. ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { words := []string{"cartwheel", "foobar", "wheel", "baz"} ranks := fuzzy.RankFind("whl", words) for _, r := range ranks { fmt.Printf("Source: %s, Target: %s, Distance: %d, Index: %d\n", r.Source, r.Target, r.Distance, r.OriginalIndex) } // Output: // Source: whl, Target: cartwheel, Distance: 6, Index: 0 // Source: whl, Target: wheel, Distance: 2, Index: 2 } ``` -------------------------------- ### Install fuzzysearch Go Package Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/00-START-HERE.md Command to install the fuzzysearch Go package using the go get command. ```bash go get github.com/lithammer/fuzzysearch ``` -------------------------------- ### Example Usage of RankFind and Sorting Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/rankFind.md Demonstrates how to use RankFind to find fuzzy matches and then sort the results by Levenshtein distance using sort.Sort(). ```go package main import ( "fmt" "sort" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { words := []string{"cartwheel", "foobar", "wheel", "baz"} // Find all matches for "whl" ranks := fuzzy.RankFind("whl", words) // ranks contains: // {Source:"whl", Target:"cartwheel", Distance:6, OriginalIndex:0} // {Source:"whl", Target:"wheel", Distance:2, OriginalIndex:2} // Sort by distance (ascending) sort.Sort(ranks) // After sorting: // {Source:"whl", Target:"wheel", Distance:2, OriginalIndex:2} // {Source:"whl", Target:"cartwheel", Distance:6, OriginalIndex:0} for _, r := range ranks { fmt.Printf("Target: %s, Distance: %d\n", r.Target, r.Distance) } } ``` -------------------------------- ### Fuzzy Matching Example Execution Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/algorithm-details.md Illustrates the step-by-step execution of the fuzzy matching algorithm for 'whl' in 'cartwheel'. ```text Initial state: source = "whl" target = "cartwheel" Step 1: Find 'w' Scan target: c, a, r, t, w ← Found at position 4 target becomes "heel" source becomes "hl" Step 2: Find 'h' Scan remaining target: h ← Found at position 0 target becomes "eel" source becomes "l" Step 3: Find 'l' Scan remaining target: e, e, l ← Found at position 2 target becomes "" source becomes "" Result: true (all source characters found in order) ``` -------------------------------- ### Example: Get Best Match Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Demonstrates how to retrieve the single best match from a list of fuzzy search results. ```APIDOC ## Get only the best match ### Description This example shows how to find all fuzzy matches, sort them by distance (lowest first), and then select the best match if any exist. ### Code Example ```go matches := fuzzy.RankFind(query, items) sort.Sort(matches) if len(matches) > 0 { best := matches[0] // The match with the lowest distance } ``` ``` -------------------------------- ### RankMatch Examples Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/rankMatch.md Demonstrates various use cases of the RankMatch function, including partial matches, no matches, and exact matches, showing the resulting Levenshtein distances. ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { fmt.Println(fuzzy.RankMatch("cart", "cartwheel")) // 5 - need to skip 5 chars fmt.Println(fuzzy.RankMatch("twl", "cartwheel")) // 6 - need to skip 6 chars fmt.Println(fuzzy.RankMatch("kitten", "sitting")) // -1 - no match fmt.Println(fuzzy.RankMatch("exact", "exact")) // 0 - exact match fmt.Println(fuzzy.RankMatch("a", "apple")) // 4 - skip 4 chars in 'apple' } ``` -------------------------------- ### Find Matches and Sort by Quality (Go) Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Use `RankFind` to get ranked matches and then `sort.Sort` to order them by distance. This is useful when you need to display the best matches first. ```go import "sort" matches := fuzzy.RankFind(query, items) sort.Sort(matches) // Sorts by distance, ascending for _, m := range matches { fmt.Printf("%s (distance: %d)\n", m.Target, m.Distance) } ``` -------------------------------- ### Spell Checker with Match and RankFind Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Use Match for a quick yes/no check. If no match, use RankFind to get suggestions. ```go if !fuzzy.Match(word, dictionary) { suggestions := fuzzy.RankFind(word, dictionary) // Show top 3 suggestions } ``` -------------------------------- ### Example: Case-Insensitive Search Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Demonstrates how to perform a case-insensitive fuzzy search, suitable for user-generated content like product names where casing variations are common. ```APIDOC ## Case-insensitive search for user content ### Description For case-insensitive searches, utilize the functions with the `Fold` suffix, such as `FindFold`. This allows matches regardless of the case of the characters in the query or the target strings. ### Code Example ```go // Use *Fold variants results := fuzzy.FindFold("apple", productNames) // Matches "APPLE", "Apple", etc. ``` ``` -------------------------------- ### Quick Example of fuzzysearch Go Package Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/00-START-HERE.md Demonstrates basic usage of the fuzzysearch package for checking matches, finding strings in a list, and ranking matches. Ensure the 'sort' package is imported for sorting ranked results. ```go import "github.com/lithammer/fuzzysearch/fuzzy" import "sort" // Check if pattern exists fuzzy.Match("whl", "cartwheel") // true // Find in list fuzzy.Find("whl", []string{"cartwheel", "wheel", "door"}) // [cartwheel wheel] // Find with ranking matches := fuzzy.RankFind("whl", []string{"cartwheel", "wheel"}) sort.Sort(matches) // Best matches first ``` -------------------------------- ### RankFindNormalizedFold Example Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/rankFind.md Demonstrates how to use RankFindNormalizedFold to find matches for a pattern with variations in diacritics and case. The results are sorted by distance. ```go package main import ( "fmt" "sort" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { words := []string{"limón", "limon", "lemon", "LIMON"} ranks := fuzzy.RankFindNormalizedFold("limó", words) // Returns matches for "limó" with both normalization and case folding // Matches: "limón" (distance 1), "limon" (distance 2), "LIMON" (distance 5) sort.Sort(ranks) for _, r := range ranks { fmt.Printf("Target: %s, Distance: %d\n", r.Target, r.Distance) } // Output: // Target: limón, Distance: 1 // Target: limon, Distance: 2 // Target: LIMON, Distance: 5 } ``` -------------------------------- ### Example: Case-Sensitive Search Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Shows how to perform a case-sensitive fuzzy search, typically used for code identifiers or situations where exact casing is important. ```APIDOC ## Case-sensitive fuzzy search for code ### Description To perform a case-sensitive fuzzy search, use the base functions like `Match`, `Find`, `RankMatch`, or `RankFind` without any case-modifying suffixes (like `Fold`). This preserves the original casing in comparisons. ### Code Example ```go // Use Match, Find, or RankMatch/RankFind without *Fold suffix // Preserves case sensitivity results := fuzzy.Find("MyVar", codeIdentifiers) ``` ``` -------------------------------- ### Basic Fuzzy Matching Examples Source: https://github.com/lithammer/fuzzysearch/blob/master/README.md Demonstrates various fuzzy matching scenarios using fuzzy.Match. Returns true if a match is found, false otherwise. Useful for quick checks against a single string. ```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 } ``` -------------------------------- ### API Functions Reference Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/DOCUMENTATION-SUMMARY.md Detailed reference for all 16 exported functions, including signatures, parameters, return values, and usage examples. ```APIDOC ## API Functions All 16 exported functions are documented with: - Full signature as code block - Parameter table (name, type, required, default, description) - Return type and meaning - Behavior description - At least one complete usage example - Source file and line reference ``` -------------------------------- ### Levenshtein Distance Example Execution Table Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/algorithm-details.md This table illustrates the step-by-step computation of the Levenshtein distance between 'kitten' and 'sitting'. It shows the intermediate values in the distance matrix, leading to the final result. ```text "" s i t t i n g "" 0 1 2 3 4 5 6 7 k 1 1 2 3 4 5 6 7 i 2 2 1 2 3 4 5 6 t 3 3 2 1 2 3 4 5 t 4 4 3 2 1 2 3 4 e 5 5 4 3 2 2 3 4 n 6 6 5 4 3 3 2 3 Final value: column[6] = 3 ``` -------------------------------- ### Sorting Ranked Fuzzy Search Results Source: https://github.com/lithammer/fuzzysearch/blob/master/README.md Illustrates how to sort the results of fuzzy.RankFind using Go's standard 'sort' package. The example shows sorting the matches in ascending order of their rank. ```go import "sort" // ... matches := fuzzy.RankFind("whl", words) // [{whl cartwheel 6 0} {whl wheel 2 2}] sort.Sort(matches) // [{whl wheel 2 2} {whl cartwheel 6 0}] ``` -------------------------------- ### Example: Filter by Minimum Match Quality Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Illustrates how to filter fuzzy search results to include only those that meet a minimum quality threshold (i.e., have a distance less than or equal to a specified value). ```APIDOC ## Require minimum match quality ### Description This example demonstrates filtering fuzzy search results to keep only those matches where the Levenshtein distance is below a certain threshold, ensuring a minimum quality of match. ### Code Example ```go matches := fuzzy.RankFind(query, items) sort.Sort(matches) var goodMatches []string for _, m := range matches { if m.Distance <= 2 { // Example threshold: distance of 2 or less goodMatches = append(goodMatches, m.Target) } } ``` ``` -------------------------------- ### RankMatch Example: Matching 'whl' in 'cartwheel' Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/algorithm-details.md Illustrates the RankMatch algorithm's character deletion counting process. It shows how 'whl' is matched in 'cartwheel' by skipping and deleting characters from the target string. ```text source = "whl", target = "cartwheel" Scan for 'w': Skip 'c', 'a', 'r', 't' (4 characters) Find 'w' target = "heel" Scan for 'h': Find 'h' immediately (0 characters skipped) target = "eel" Scan for 'l': Skip 'e', 'e' (2 characters) Find 'l' target = "" Total distance = 4 + 0 + 2 = 6 (These are the 6 characters that need to be deleted from "cartwheel" to get "whl") ``` -------------------------------- ### Calculate String Similarity Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/levenshtein.md This example shows how to calculate a similarity score between two strings based on their Levenshtein distance. A score of 1.0 indicates identical strings, while 0.0 indicates maximum dissimilarity relative to the longer string's length. ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) func similarity(s1, s2 string) float64 { max := len(s1) if len(s2) > max { max = len(s2) } if max == 0 { return 1.0 } distance := fuzzy.LevenshteinDistance(s1, s2) return 1.0 - float64(distance)/float64(max) } func main() { fmt.Printf("%.2f\n", similarity("kitten", "sitting")) // 0.50 fmt.Printf("%.2f\n", similarity("hello", "hello")) // 1.00 fmt.Printf("%.2f\n", similarity("abc", "xyz")) // 0.00 } ``` -------------------------------- ### Get Best Fuzzy Match from List Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Finds all fuzzy matches for a query within a list of items, sorts them by distance (best match first), and retrieves the best match if any exist. Assumes `items` is a slice of strings. ```go matches := fuzzy.RankFind(query, items) sort.Sort(matches) if len(matches) > 0 { best := matches[0] // Lowest distance } ``` -------------------------------- ### Practical Usage Pattern: Find, Rank, and Sort Matches Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/ranks.md Illustrates a common workflow for fuzzy searching: finding all matches with RankFind, sorting them by distance, and then processing the results. ```go package main import ( "fmt" "sort" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { candidates := []string{"apple", "application", "apply", "appointment", "approve"} query := "app" // Find and rank all matches matches := fuzzy.RankFind(query, candidates) // Sort by distance (best matches first) sort.Sort(matches) // Process results in order for i, match := range matches { rank := i + 1 fmt.Printf("#%d: %s (distance: %d)\n", rank, match.Target, match.Distance) } // Output: // #1: apply (distance: 1) // #2: apple (distance: 2) // #3: approve (distance: 3) // #4: appointment (distance: 4) // #5: application (distance: 5) } ``` -------------------------------- ### Autocomplete with Ranking in Go Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/patterns-and-examples.md Builds an autocomplete system that returns the top N best matches for a given query from a list of candidates. It uses fuzzy ranking and sorting. ```go package main import ( "fmt" "sort" "github.com/lithammer/fuzzysearch/fuzzy" ) // Autocomplete returns the best N matches for a query from the candidates. func Autocomplete(query string, candidates []string, maxResults int) []string { ranks := fuzzy.RankFindNormalizedFold(query, candidates) sort.Sort(ranks) results := make([]string, 0, maxResults) for i, r := range ranks { if i >= maxResults { break } results = append(results, r.Target) } return results } func main() { commands := []string{ "git", "git add", "git commit", "git log", "git branch", "go", "go build", "go run", "go test", } query := "go" matches := Autocomplete(query, commands, 3) fmt.Printf("Best matches for '%s':\n", query) for _, m := range matches { fmt.Printf(" - %s\n", m) } // Output shows the 3 best matches for "go" } ``` -------------------------------- ### Import Fuzzy Search Package Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/INDEX.md Import the fuzzy search package to use its functionalities in your Go project. ```go import "github.com/lithammer/fuzzysearch/fuzzy" ``` -------------------------------- ### RankMatchNormalizedFold Example Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/rankMatch.md Demonstrates how to use RankMatchNormalizedFold for Unicode-normalized and case-insensitive matching. This function is useful when dealing with international characters or variations in capitalization. It returns the Levenshtein distance for matches and -1 for no match. ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { fmt.Println(fuzzy.RankMatchNormalizedFold("limó", "limon")) // 1 fmt.Println(fuzzy.RankMatchNormalizedFold("limó", "LIMON")) // 1 fmt.Println(fuzzy.RankMatchNormalizedFold("limó", "LIMON TART")) // 6 fmt.Println(fuzzy.RankMatchNormalizedFold("KITTEN", "sitting")) // -1 - no match } ``` -------------------------------- ### Calculate Exact Levenshtein Distance (Go) Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Get the exact Levenshtein edit distance between two strings using `LevenshteinDistance`. The result is an integer representing the number of single-character edits required. ```go distance := fuzzy.LevenshteinDistance("kitten", "sitting") // 3 ``` -------------------------------- ### Spell Checker with Fuzzy Suggestions (Go) Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/patterns-and-examples.md Implement a spell checker that verifies if a word exists in a dictionary and provides suggestions for misspelled words. Suggestions are limited to words with a Levenshtein distance of 2 or less, and a maximum of three suggestions are returned. ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) // CheckSpelling verifies and suggests corrections for a word. type SpellChecker struct { dictionary []string } func NewSpellChecker(words []string) *SpellChecker { return &SpellChecker{dictionary: words} } func (sc *SpellChecker) Check(word string) (isCorrect bool, suggestions []string) { // Check if word exists exactly for _, w := range sc.dictionary { if w == word { return true, nil } } // Find close matches as suggestions ranks := fuzzy.RankMatch(word, "") matches := fuzzy.RankFindNormalizedFold(word, sc.dictionary) for i, r := range matches { // Only suggest words that are reasonably similar (distance <= 2) if r.Distance <= 2 { if i >= 3 { break // Limit to 3 suggestions } suggestions = append(suggestions, r.Target) } } return false, suggestions } func main() { dictionary := []string{ "apple", "application", "apply", "applaud", "appreciate", } checker := NewSpellChecker(dictionary) word := "aple" correct, suggestions := checker.Check(word) if correct { fmt.Printf("'%s' is correctly spelled.\n", word) } else { fmt.Printf("'%s' is not in dictionary. Suggestions: %v\n", word, suggestions) } } ``` -------------------------------- ### Suggest Correction for Misspelled Word Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/levenshtein.md This example demonstrates how to use LevenshteinDistance to find the closest word in a dictionary for a given misspelled word. It considers words with a distance of 2 or less as potential corrections. ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) func suggestCorrection(misspelled string, dictionary []string) string { minDistance := int(^uint(0) >> 1) // max int var best string for _, word := range dictionary { distance := fuzzy.LevenshteinDistance(misspelled, word) if distance < minDistance && distance <= 2 { minDistance = distance best = word } } return best } func main() { dict := []string{"apple", "application", "apply"} fmt.Println(suggestCorrection("aple", dict)) // apple fmt.Println(suggestCorrection("aplly", dict)) // apply } ``` -------------------------------- ### Fuzzy Search Performance Optimization Techniques Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/patterns-and-examples.md Demonstrates strategies to optimize fuzzy search performance for large datasets. FastSearchWithLimit restricts the number of items processed, while PrefixFilter pre-filters exact prefix matches. ```go package main import ( "fmt" "sort" "github.com/lithammer/fuzzysearch/fuzzy" ) // FastSearchWithLimit limits processing to speed up very large lists func FastSearchWithLimit(query string, items []string, maxProcessed int) []string { // Only process up to maxProcessed items (e.g., for pagination) processed := maxProcessed if processed > len(items) { processed = len(items) } return fuzzy.Find(query, items[:processed]) } // PrefixFilter pre-filters items before fuzzy matching // Useful when you have a prefix that matches exactly func PrefixFilter(query string, items []string) []string { // Fast prefix filter first var prefixMatches []string for _, item := range items { if len(item) >= len(query) && item[:len(query)] == query { prefixMatches = append(prefixMatches, item) } } // If we have prefix matches, use only those if len(prefixMatches) > 0 { return prefixMatches } // Fall back to fuzzy matching return fuzzy.Find(query, items) } // CacheResults demonstrates caching for repeated queries type SearchCache struct { items map[string][]string } func NewSearchCache() *SearchCache { return &SearchCache{ items: make(map[string][]string), } } func (sc *SearchCache) Find(query string, candidates []string) []string { // Check cache if results, ok := sc.items[query]; ok { return results } // Perform search results := fuzzy.FindFold(query, candidates) // Cache results sc.items[query] = results return results } func main() { items := []string{ "apple", "apricot", "application", "apply", "banana", "band", "bandana", "car", "cart", "carrot", } // Demonstrate prefix filtering fmt.Println("Prefix-optimized search for 'app':") fmt.Println(PrefixFilter("app", items)) // Demonstrate caching cache := NewSearchCache() fmt.Println("\nFirst search for 'car':") fmt.Println(cache.Find("car", items)) fmt.Println("Second search for 'car' (from cache):") fmt.Println(cache.Find("car", items)) } ``` -------------------------------- ### Edge Cases: Empty Strings Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Demonstrates the behavior of fuzzy.Match with empty strings. ```go // Empty strings fuzzy.Match("", "") // true - empty matches empty fuzzy.Match("a", "") // false - non-empty doesn't match empty fuzzy.Match("", "a") // true - empty matches anything ``` -------------------------------- ### RankFind Function Signature Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/rankFind.md The RankFind function takes a source string and a slice of target strings, returning a slice of Ranks. ```go func RankFind(source string, targets []string) Ranks ``` -------------------------------- ### Fuzzy Search - Go Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/find.md Use `fuzzy.Find` to get a list of strings from a target slice that fuzzy match a source pattern. The results maintain the order of the input slice. Returns an empty slice if no matches are found. ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { words := []string{"cartwheel", "foobar", "wheel", "baz"} results := fuzzy.Find("whl", words) fmt.Println(results) // [cartwheel wheel] results = fuzzy.Find("xyz", words) fmt.Println(results) // [] results = fuzzy.Find("bar", words) fmt.Println(results) // [foobar] } ``` -------------------------------- ### Find All Matching Strings in a List Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Use these functions to find all strings within a list that match the given pattern. Options for case sensitivity and Unicode normalization are available. ```go fuzzy.Find(source string, targets []string) []string ``` ```go fuzzy.FindFold(source string, targets []string) []string ``` ```go fuzzy.FindNormalized(source string, targets []string) []string ``` ```go fuzzy.FindNormalizedFold(source string, targets []string) []string ``` -------------------------------- ### Find and Rank All Matching Strings in a List Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md These functions find all matching strings in a list and return them along with their calculated ranks (edit distances). Options for case sensitivity and Unicode normalization are available. ```go fuzzy.RankFind(source string, targets []string) Ranks ``` ```go fuzzy.RankFindFold(source string, targets []string) Ranks ``` ```go fuzzy.RankFindNormalized(source string, targets []string) Ranks ``` ```go fuzzy.RankFindNormalizedFold(source string, targets []string) Ranks ``` -------------------------------- ### Product Search with FindFold Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Use FindFold for general searching and filtering of a product catalog. ```go results := fuzzy.FindFold(query, productCatalog) ``` -------------------------------- ### Edge Cases: Distance Calculations Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Illustrates distance calculations for empty strings and RankMatch. ```go // Distance fuzzy.LevenshteinDistance("", "") // 0 fuzzy.LevenshteinDistance("a", "") // 1 fuzzy.RankMatch("a", "") // -1 (no match) ``` -------------------------------- ### Complete Search Implementation in Go Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/patterns-and-examples.md Implements a production-ready search function that performs case-insensitive, Unicode-normalized fuzzy searches. Results are ranked by Levenshtein distance and sorted. ```go package main import ( "fmt" "sort" "strings" "github.com/lithammer/fuzzysearch/fuzzy" ) type SearchResult struct { Item string Distance int Index int } type SearchResults []SearchResult func (s SearchResults) Len() int { return len(s) } func (s SearchResults) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s SearchResults) Less(i, j int) bool { return s[i].Distance < s[j].Distance } // Search performs a case-insensitive, Unicode-normalized fuzzy search. // Results are ranked by Levenshtein distance (ascending). func Search(query string, items []string) SearchResults { ranks := fuzzy.RankFindNormalizedFold(query, items) results := make(SearchResults, len(ranks)) for i, r := range ranks { results[i] = SearchResult{ Item: r.Target, Distance: r.Distance, Index: r.OriginalIndex, } } sort.Sort(results) return results } func main() { items := []string{ "Application", "Apple", "Approval", "Appointment", "Applied", } results := Search("APP", items) for i, r := range results { fmt.Printf("%d. %s (distance: %d)\n", i+1, r.Item, r.Distance) } // Output shows all items starting with "app" in best-match order } ``` -------------------------------- ### Find Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/find.md Returns a list of strings from the targets array that fuzzy match the source pattern. ```APIDOC ## Find ### Description Returns a list of strings from the targets array that fuzzy match the source pattern. ### Function Signature ```go func Find(source string, targets []string) []string ``` ### Parameters #### Path Parameters - **source** (string) - Required - The pattern to search for - **targets** ([]string) - Required - The list of strings to search within ### Return Type `[]string` - A slice of strings from targets that match source. The returned slice is in the same order as the input array. Returns an empty slice if no matches are found. ### Behavior Iterates through each string in targets and returns those that match the source using the basic fuzzy matching algorithm (same as `Match`). The order of results matches the order of the input slice. ### Example ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { words := []string{"cartwheel", "foobar", "wheel", "baz"} results := fuzzy.Find("whl", words) fmt.Println(results) // [cartwheel wheel] results = fuzzy.Find("xyz", words) fmt.Println(results) // [] results = fuzzy.Find("bar", words) fmt.Println(results) // [foobar] } ``` ``` -------------------------------- ### Basic Fuzzy Search Functions Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md This section covers the core functions for fuzzy searching: Match, RankMatch, and Find. It also explains how to modify behavior for case and accent insensitivity. ```APIDOC ## Match() ### Description Checks if a pattern exists within a target string, considering fuzzy matching rules. ### Method `Match(pattern, target)` ### Parameters - **pattern** (string) - Required - The string to search for. - **target** (string) - Required - The string to search within. ### Response - **bool** - Returns true if a match is found, false otherwise. ## RankMatch() ### Description Returns an integer representing the quality score of a match between a pattern and a target string. Higher scores indicate better matches. ### Method `RankMatch(pattern, target)` ### Parameters - **pattern** (string) - Required - The string to search for. - **target** (string) - Required - The string to search within. ### Response - **int** - The match quality score. ## Find() ### Description Filters a list of items, returning only those that fuzzily match the given query. ### Method `Find(query, items)` ### Parameters - **query** (string) - Required - The search query. - **items** ([]string) - Required - The list of items to filter. ### Response - **[]string** - A list of items that match the query. ## Transformations ### Fold Suffix Append `Fold` to function names (e.g., `FindFold`) for case-insensitive matching. ### Normalized Suffix Append `Normalized` to function names for accent-insensitive matching. ### NormalizedFold Suffix Append `NormalizedFold` to function names for both case and accent-insensitive matching. ``` -------------------------------- ### Unicode Normalized Fuzzy Matching Source: https://github.com/lithammer/fuzzysearch/blob/master/README.md Demonstrates fuzzy.MatchNormalized for matching strings that have been Unicode normalized. This ensures accurate comparisons with different character representations. ```go // Unicode normalized matching. fuzzy.MatchNormalized("cartwheel", "cartwhéél") // true ``` -------------------------------- ### Rune Slicing for Advancement Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/algorithm-details.md Uses `utf8.RuneLen()` to correctly advance past multi-byte characters when slicing strings. This ensures accurate character processing. ```Go target = target[i+utf8.RuneLen(r2):] // Skip the matched rune ``` -------------------------------- ### Fuzzy Matching with Ranking Source: https://github.com/lithammer/fuzzysearch/blob/master/README.md Uses fuzzy.RankMatch to find matches and return a score. A score of -1 indicates no match. Higher scores represent better matches. ```go fuzzy.RankMatch("kitten", "sitting") // -1 fuzzy.RankMatch("cart", "cartwheel") // 5 ``` -------------------------------- ### Rank Matches by Quality Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/README.md Finds matches in a list and ranks them by quality, with lower distance indicating a better match. Requires sorting the results. ```go import ( "sort" "github.com/lithammer/fuzzysearch/fuzzy" ) words := []string{"cartwheel", "foobar", "wheel", "baz"} ranks := fuzzy.RankFind("whl", words) // Sort by distance (best matches first) sort.Sort(ranks) for _, r := range ranks { fmt.Printf("%s (distance: %d)\n", r.Target, r.Distance) } // Output: // wheel (distance: 2) // cartwheel (distance: 6) ``` -------------------------------- ### Core Concepts Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/DOCUMENTATION-SUMMARY.md Explanations of key concepts related to fuzzy searching, including algorithms, ranking, and Unicode handling. ```APIDOC ## Core Concepts - **Fuzzy Matching:** What it is, how it works, when to use - **Levenshtein Distance:** Algorithm explanation, use cases, complexity - **Ranking:** How distance-based ranking works, practical examples - **Unicode:** Normalization strategy, case folding, handling details - **Variants:** When to use each (standard, Fold, Normalized, NormalizedFold) - **Performance:** Complexity analysis, optimization strategies, tips ``` -------------------------------- ### Using MatchNormalizedFold for Fuzzy Matching Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/match.md Demonstrates how to use MatchNormalizedFold with different casing and diacritical marks. This function is useful for flexible string comparisons where variations in case and accents should be ignored. ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { fmt.Println(fuzzy.MatchNormalizedFold("limón", "LIMON")) // true fmt.Println(fuzzy.MatchNormalizedFold("LIMON", "limón tart")) // true fmt.Println(fuzzy.MatchNormalizedFold("limón", "LiMóN tArT")) // true } ``` -------------------------------- ### Sorting Ranks by Distance Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/types.md Demonstrates how to sort a slice of `Rank` objects by their `Distance` field in ascending order using Go's `sort` package. Ensure the `Ranks` type is correctly imported and used. ```go import "sort" ranks := fuzzy.RankFind(query, targets) sort.Sort(ranks) // Sorts by distance, ascending ``` -------------------------------- ### Unicode Normalization Strategy Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/algorithm-details.md Illustrates the Unicode normalization process using NFD, Remove Mn, and NFC for canonical representation. This process ensures consistent string comparison. ```Text source string ↓ norm.NFD // Decompose composite characters ↓ runes.Remove() // Remove combining marks (category Mn) ↓ norm.NFC // Recompose into canonical form ↓ normalized string ``` -------------------------------- ### Case Insensitive Fuzzy Matching Source: https://github.com/lithammer/fuzzysearch/blob/master/README.md Shows how to perform case-insensitive matching using fuzzy.MatchFold. This is useful when the case of the input or target string should not affect the match. ```go // Case insensitive matching. fuzzy.MatchFold("ArTeeL", "cartwheel") // true ``` -------------------------------- ### Edge Cases: Unicode Normalization Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Shows the difference between normalized and non-normalized Unicode matching. ```go // Unicode fuzzy.MatchNormalized("cafe", "café") // true fuzzy.Match("cafe", "café") // false ``` -------------------------------- ### RankFind() and Variants Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/_FILES-GUIDE.txt Functions for finding and ranking occurrences of a pattern within a list of strings. These functions return a ranked list of matches. ```APIDOC ## RankFind() and Variants ### Description These functions find and rank occurrences of a pattern within a list of strings. They offer options for case sensitivity and Unicode normalization, returning a sorted list of ranked matches. ### Functions - `RankFind(pattern string, list []string) Ranks` - `RankFindFold(pattern string, list []string) Ranks` - `RankFindNormalized(pattern string, list []string) Ranks` - `RankFindNormalizedFold(pattern string, list []string) Ranks` ### Parameters - **pattern** (string) - The pattern to search for. - **list** ([]string) - The list of strings to search within. ### Returns - **Ranks** - A `Ranks` object (a slice of `Rank` objects) containing the ranked matches, sorted by score. ### Coverage Signatures, parameters, returns, behavior, examples, source location are documented in `api-reference/rankFind.md`. ``` -------------------------------- ### Check if Pattern Matches String (Boolean) Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Use these functions to check if a pattern matches a target string. Options include case-sensitive, case-insensitive, Unicode normalization, and combined normalization/case-insensitivity. ```go fuzzy.Match(source, target string) bool ``` ```go fuzzy.MatchFold(source, target string) bool // Case-insensitive ``` ```go fuzzy.MatchNormalized(source, target string) bool // Unicode normalization ``` ```go fuzzy.MatchNormalizedFold(source, target string) bool // Both ``` -------------------------------- ### Case Folding Implementation Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/algorithm-details.md Implements Unicode case folding using `unicodeFoldTransformer` and `unicode.ToLower()`. Handles complex Unicode case rules automatically. ```Go type unicodeFoldTransformer struct{ transform.NopResetter } func (unicodeFoldTransformer) Transform(dst, src []byte, atEOF bool) (...) { for _, r := range string(src) { r = unicode.ToLower(r) // Apply Unicode case folding // ... encode the rune and advance pointers } ... } ``` -------------------------------- ### Basic Fuzzy Matching Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Tests if a query string matches a target string using basic fuzzy matching. Returns true if a match is found. ```go // Test basic matching assert := fuzzy.Match("twl", "cartwheel") == true ``` -------------------------------- ### Multi-Field Fuzzy Search in Go Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/patterns-and-examples.md Implement fuzzy search across multiple fields (title, description, tags) of a document. Results are ranked by relevance, with title matches weighted higher. Use this when you need to find documents based on a query that could appear in various parts of the document content. ```go package main import ( "fmt" "sort" "github.com/lithammer/fuzzysearch/fuzzy" ) type Document struct { Title string Description string Tags []string } type DocumentMatch struct { Doc *Document Score int // Lower is better MatchedIn string } type DocumentMatches []DocumentMatch func (dm DocumentMatches) Len() int { return len(dm) } func (dm DocumentMatches) Swap(i, j int) { dm[i], dm[j] = dm[j], dm[i] } func (dm DocumentMatches) Less(i, j int) bool { return dm[i].Score < dm[j].Score } // SearchDocuments searches across title, description, and tags. func SearchDocuments(query string, docs []*Document) DocumentMatches { var matches DocumentMatches for _, doc := range docs { // Search title (weighted as most important) if rank := fuzzy.RankMatchNormalizedFold(query, doc.Title); rank >= 0 { matches = append(matches, DocumentMatch{ Doc: doc, Score: rank * 2, // Weight title matches higher MatchedIn: "title", }) continue } // Search description if rank := fuzzy.RankMatchNormalizedFold(query, doc.Description); rank >= 0 { matches = append(matches, DocumentMatch{ Doc: doc, Score: rank * 1, MatchedIn: "description", }) continue } // Search tags for _, tag := range doc.Tags { if fuzzy.MatchNormalizedFold(query, tag) { matches = append(matches, DocumentMatch{ Doc: doc, Score: 10, MatchedIn: "tag", }) break } } } sort.Sort(matches) return matches } func main() { docs := []*Document{ { Title: "Go Language Guide", Description: "Learn the Go programming language", Tags: []string{"golang", "programming"}, }, { Title: "JavaScript Basics", Description: "Introduction to JavaScript and web development", Tags: []string{"javascript", "web"}, }, } results := SearchDocuments("go", docs) for _, m := range results { fmt.Printf("Found in %s: %s (score: %d)\n", m.MatchedIn, m.Doc.Title, m.Score) } } ``` -------------------------------- ### Unicode Transformation Pipeline Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/algorithm-details.md Details the sequence of Unicode transformations applied for normalization and case folding. ```text Input string ↓ [1] NFD Normalization (decompose characters) Example: "café" → "cafe´" ↓ [2] Remove combining marks (category Mn) Example: "cafe´" → "cafe" ↓ [3] NFC Normalization (recompose) Example: "cafe" → "cafe" ↓ [4] Case Folding (convert to lowercase with Unicode rules) Example: "CAFE" → "cafe" ↓ Output string ``` -------------------------------- ### Case-Insensitive Search (Go) Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Perform a case-insensitive search using `FindFold`. This function returns a slice of matching strings, preserving the order from the input. ```go matches := fuzzy.FindFold(query, items) ``` -------------------------------- ### Ranked Search in a List of Words Source: https://github.com/lithammer/fuzzysearch/blob/master/README.md Utilizes fuzzy.RankFind to search a list of words and return ranked matches. Each result includes the matched string and its score. ```go fuzzy.RankFind("whl", words) // [{whl cartwheel 6 0} {whl wheel 2 2}] ``` -------------------------------- ### Convert String to Rune Slice Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/algorithm-details.md Converts strings to rune slices for correct multi-byte UTF-8 sequence processing. Ensures one rune equals one logical character. ```Go r1, r2 := []rune(s), []rune(t) ``` -------------------------------- ### Autocomplete with RankFindNormalizedFold Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/quick-reference.md Use RankFindNormalizedFold for autocomplete suggestions. Sort the results and take the top N. ```go matches := fuzzy.RankFindNormalizedFold(query, allItems) sort.Sort(matches) // Take first 5-10 ``` -------------------------------- ### Matching Functions Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/INDEX.md Provides basic pattern matching capabilities with options for case sensitivity, Unicode normalization, and folding. ```APIDOC ## Matching Functions ### Description Basic pattern matching functions. ### Functions - `Match(pattern string, text string) bool` - `MatchFold(pattern string, text string) bool` - `MatchNormalized(pattern string, text string) bool` - `MatchNormalizedFold(pattern string, text string) bool` ### Details Includes: signatures, parameters, return types, examples, source locations. ``` -------------------------------- ### Match Functions Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/DOCUMENTATION-SUMMARY.md Provides functions for case-sensitive and case-insensitive string matching, including options for Unicode normalization. ```APIDOC ## Match Functions ### Description Functions for performing case-sensitive and case-insensitive string matching, with support for Unicode normalization. ### Functions - **Match(a, b string) int**: Case-sensitive matching. - **MatchFold(a, b string) int**: Case-insensitive matching. - **MatchNormalized(a, b string) int**: Case-sensitive matching with Unicode normalization. - **MatchNormalizedFold(a, b string) int**: Case-insensitive matching with Unicode normalization. ### Parameters - **a** (string) - The string to search within. - **b** (string) - The string to search for. ### Return Type - **int**: The rank of the match, or -1 if no match is found. ``` -------------------------------- ### Unicode-Normalized Fuzzy Matching with MatchNormalized Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/api-reference/match.md Employ `MatchNormalized` for fuzzy matching that accounts for Unicode normalization. It converts strings to NFD, removes diacritics, and converts back to NFC, enabling matches between composed and decomposed character forms. Use this when dealing with international characters or variations in character representation. ```go package main import ( "fmt" "github.com/lithammer/fuzzysearch/fuzzy" ) func main() { fmt.Println(fuzzy.MatchNormalized("limón", "limon")) // false - different characters fmt.Println(fuzzy.MatchNormalized("limon", "limón")) // true - decomposed form matches fmt.Println(fuzzy.MatchNormalized("limón", "limon tart")) // true - prefix matches } ``` -------------------------------- ### String Transformation Caching Source: https://github.com/lithammer/fuzzysearch/blob/master/_autodocs/algorithm-details.md Optimizes string transformation by providing a fast path for no-op transformers. Returns the original string without allocation if the transformer is a no-op. ```Go // Fast path for the nop transformer if _, ok := t.(nopTransformer); ok { return s // Return original, no allocation } ```