/// <summary> /// Ищет наиболее подходящие совпадения в коллекции для указанного слова. /// Сравнение символов начинается с конца слова, чем ближе символ к началу слова, тем меньше веса совпадение имеет. /// Пример: пара [краснЫй и краснОй] имеет меньший вес совпадения, чем пара [КРАСный и УСТный]. /// </summary> /// <param name="word">Слово для поиска в коллекции.</param> /// <param name="collection">Коллекция, в которой искать совпадения.</param> /// <param name="minSameLetters">Минимальное кол-во совпадающих символов с конца слова.</param> /// <param name="maxSameLetters"> /// Максимальное кол-во совпадающих символов с конца слова. /// Можно использовать <see cref="int.MaxValue"/> для сравнения всех символов слова. /// </param> public List <string> GetSimilars(string word, IEnumerable <string> collection, int minSameLetters, int maxSameLetters) { if (word == null || word.Length < minSameLetters) { return(null); } string foundWord = null; List <SimilarCandidate> candidates = new List <SimilarCandidate>(); foreach (string str in collection) { if (str == word) { foundWord = str; break; } int sameLetters; int maxPosition = Math.Min(maxSameLetters, Math.Min(word.Length, str.Length)); int weight = this.GetSimilarityWeight(word, str, maxPosition, minSameLetters, out sameLetters); if (sameLetters >= minSameLetters) { SimilarCandidate c = new SimilarCandidate() { Name = str, Weight = weight }; candidates.Add(c); } } return(candidates .AsParallel() .OrderByDescending(x => x.Weight) .ThenBy(x => Math.Abs(word.Length - x.Name.Length)) .ThenByDescending(x => char.IsUpper(x.Name[0]) == char.IsUpper(word[0])) .Select(x => x.Name) .ToList()); }
/// <summary>Search for similar words in the collection</summary> /// <param name="Word">The word to search for</param> /// <param name="Collection">The collection to look in</param> /// <param name="MinSameLetters">The minimum number of matching letters, from the right end, min value - <see cref="DefaultMinSameLetters"/></param> public string GetSimilar(string Word, IEnumerable <string> Collection, int MinSameLetters = DefaultMinSameLetters) { if (MinSameLetters < DefaultMinSameLetters) { throw new ArgumentOutOfRangeException($"{nameof(MinSameLetters)} value can not be smaller than {DefaultMinSameLetters}!"); } if (Word == null || Word.Length < MinSameLetters) { return(Word); } string foundWord = null; ConcurrentBag <SimilarCandidate> candidates = new ConcurrentBag <SimilarCandidate>(); Parallel.ForEach(Collection, (str, loopState) => { if (str == Word) { foundWord = str; loopState.Stop(); return; } int maxPosition = Math.Min(Word.Length, str.Length); int weight = 0; for (int i = 1; i <= maxPosition; i++) { if (str[str.Length - i] == Word[Word.Length - i]) { int wi = i - 1; weight += wi < SameLetterWeights.Length ? SameLetterWeights[wi] : 1; } else if (i <= MinSameLetters) { return; } } SimilarCandidate c = new SimilarCandidate() { Name = str, Weight = weight }; candidates.Add(c); }); if (!string.IsNullOrEmpty(foundWord)) { return(foundWord); } SimilarCandidate candidate = null; foreach (SimilarCandidate c in candidates) { if (candidate == null || c.Weight > candidate.Weight || (c.Weight == candidate.Weight && c.Name.Length < candidate.Name.Length) || (c.Weight == candidate.Weight && c.Name.Length == candidate.Name.Length && c.Name.CompareTo(candidate.Name) < 0)) { candidate = c; } } return(candidate?.Name); }
/// <summary> /// Ищет наиболее подходящее совпадение в коллекции для указанного слова. /// Сравнение символов начинается с конца слова, чем ближе символ к началу слова, тем меньше веса совпадение имеет. /// Пример: пара [краснЫй и краснОй] имеет меньший вес совпадения, чем пара [КРАСный и УСТный]. /// </summary> /// <param name="word">Слово для поиска в коллекции.</param> /// <param name="collection">Коллекция, в которой искать совпадения.</param> /// <param name="minSameLetters">Минимальное кол-во совпадающих символов с конца слова.</param> /// <param name="maxSameLetters"> /// Максимальное кол-во совпадающих символов с конца слова. /// Можно использовать <see cref="int.MaxValue"/> для сравнения всех символов слова. /// </param> public string GetSimilar(string word, IEnumerable <string> collection, int minSameLetters, int maxSameLetters) { if (word == null || word.Length < minSameLetters) { return(word); } string foundWord = null; List <SimilarCandidate> candidates = new List <SimilarCandidate>(); foreach (string str in collection) { if (str == word) { foundWord = str; break; } int sameLetters; int maxPosition = Math.Min(maxSameLetters, Math.Min(word.Length, str.Length)); int weight = this.GetSimilarityWeight(word, str, maxPosition, minSameLetters, out sameLetters); if (sameLetters >= minSameLetters) { SimilarCandidate c = new SimilarCandidate() { Name = str, Weight = weight }; candidates.Add(c); } } if (!string.IsNullOrEmpty(foundWord)) { return(foundWord); } SimilarCandidate candidate = null; foreach (SimilarCandidate c in candidates) { if (candidate == null) { candidate = c; continue; } if (c.Weight > candidate.Weight) { candidate = c; continue; } bool sameWeight = c.Weight == candidate.Weight; int betterLengthDiff = Math.Abs(word.Length - c.Name.Length) - Math.Abs(word.Length - candidate.Name.Length); if (sameWeight && betterLengthDiff < 0) { candidate = c; continue; } bool sameCase = char.IsUpper(c.Name[0]) == char.IsUpper(word[0]); if (sameWeight && betterLengthDiff == 0 && sameCase) { candidate = c; continue; } } return(candidate?.Name); }