Last active
March 23, 2025 22:03
-
-
Save EsinShadrach/a628adb0b82e33698ab343a523628318 to your computer and use it in GitHub Desktop.
If you've ever felt .contains just doens't work for your usecase in dart.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // A string search algorithm that can handle typographical errors and misspellings | |
| // Returns true if there is a match or if strings are similar enough | |
| bool isFound(String string, String search) { | |
| if (search.isEmpty) return true; | |
| final String lowerString = string.toLowerCase(); | |
| final String lowerSearch = search.toLowerCase(); | |
| // Quick check for exact substring match | |
| if (lowerString.contains(lowerSearch)) return true; | |
| // Split the string into words for word-by-word comparison | |
| final List<String> words = lowerString.split(RegExp(r'\s+')); | |
| final List<String> searchWords = lowerSearch.split(RegExp(r'\s+')); | |
| // For single word searches, check similarity with each word | |
| if (searchWords.length == 1) { | |
| for (var word in words) { | |
| // For very short words, require exact match or high similarity | |
| if (word.length < 3 || lowerSearch.length < 3) { | |
| if (word == lowerSearch) return true; | |
| } | |
| // For longer words, use Levenshtein distance with threshold | |
| else { | |
| int distance = _levenshteinDistance(word, lowerSearch); | |
| int maxLength = | |
| word.length > lowerSearch.length ? word.length : lowerSearch.length; | |
| // Calculate similarity as a percentage (0-100) | |
| double similarity = (1 - distance / maxLength) * 100; | |
| // Threshold: accept if similarity is above 70% | |
| if (similarity >= 70) return true; | |
| } | |
| } | |
| } | |
| // For multi-word searches, check if enough words match | |
| else { | |
| int matchedWords = 0; | |
| for (var searchWord in searchWords) { | |
| if (searchWord.length < 2) continue; // Skip very short search terms | |
| for (var word in words) { | |
| if (word.contains(searchWord) || | |
| (_levenshteinDistance(word, searchWord) <= 2 && | |
| searchWord.length > 3)) { | |
| matchedWords++; | |
| break; | |
| } | |
| } | |
| } | |
| // Return true if at least half of the search words match | |
| return matchedWords >= (searchWords.length / 2).ceil(); | |
| } | |
| return false; | |
| } | |
| // Calculate Levenshtein distance between two strings | |
| // Returns the minimum number of single-character edits needed to change one string to another | |
| int _levenshteinDistance(String s, String t) { | |
| if (s == t) return 0; | |
| if (s.isEmpty) return t.length; | |
| if (t.isEmpty) return s.length; | |
| List<int> v0 = List<int>.filled(t.length + 1, 0); | |
| List<int> v1 = List<int>.filled(t.length + 1, 0); | |
| for (int i = 0; i <= t.length; i++) { | |
| v0[i] = i; | |
| } | |
| for (int i = 0; i < s.length; i++) { | |
| v1[0] = i + 1; | |
| for (int j = 0; j < t.length; j++) { | |
| int cost = (s[i] == t[j]) ? 0 : 1; | |
| v1[j + 1] = [v1[j] + 1, v0[j + 1] + 1, v0[j] + cost] | |
| .reduce((a, b) => a < b ? a : b); | |
| } | |
| for (int j = 0; j <= t.length; j++) { | |
| v0[j] = v1[j]; | |
| } | |
| } | |
| return v1[t.length]; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment