Skip to content

Instantly share code, notes, and snippets.

@EsinShadrach
Last active March 23, 2025 22:03
Show Gist options
  • Select an option

  • Save EsinShadrach/a628adb0b82e33698ab343a523628318 to your computer and use it in GitHub Desktop.

Select an option

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.
// 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