最精确的结果在我的搜索应用程序

时间:2018-04-02 11:58:11

标签: c# oop

我的新列表(file.txt)中有几句话。例如:

  • 沃尔特·迪斯尼拒绝允许阿尔弗雷德·希区柯克在20世纪60年代早期在迪斯尼乐园拍摄,因为他制作了“那部令人作呕的电影”心理学“。
  • 狮子王中的Pumbaa是迪士尼电影中放屁的第一个角色。
  • 华特迪士尼向白雪公主和七个小矮人的动画师支付了5美元,因为这些动画片已经成为电影的最终版本。
  • 电影“十六蜡烛”中的蛋糕是用纸板制成的。

我想在我的listBox中只显示包含在搜索框中输入的任何单词的句子。例如:当我输入" disney七个小矮人"时,它应该显示" Walt Disney支付了“白雪公主”和“七个小矮人”的动画师5美元,因为任何插曲都会成为电影的最终版本。"在列表的顶部。它不应该显示"电影十六蜡烛中的蛋糕由纸板制成。",因为这句话不包含任何输入的单词。简而言之:在顶部应该显示具有最多匹配单词的结果。

public static IEnumerable<string> SplitSearchWords(string str)
{
 int charIndex = 0;
 int wordStart = 0;
 while (charIndex < str.Length)
 {
    wordStart = charIndex;
    if (char.IsLetterOrDigit(str[charIndex]))
    {
        while (charIndex < str.Length && char.IsLetterOrDigit(str[charIndex])) charIndex++;
        yield return str.Substring(wordStart, charIndex-wordStart);
    }
    else
    {
        while (charIndex < str.Length && !char.IsLetterOrDigit(str[charIndex])) charIndex++;
    }
  }
}

public static int CalculateSearchRelevance(string searchItem, IEnumerable<string> searchWords)
{
  var searchItemWords = SplitSearchWords(searchItem);
  return searchWords.Intersect(searchItemWords, StringComparer.OrdinalIgnoreCase).Count();
}

var myFile = File.ReadAllLines("file.txt");
var myList = new List<string>(myFile);

var query = textBox1.Text;
var items = myList;

var searchWords = SplitSearchWords(query).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
var sortedItems = items.OrderByDescending(s => CalculateSearchRelevance(s, searchWords)).ToList();

3 个答案:

答案 0 :(得分:0)

您可以使用String.Contains()方法并构建自定义函数来确定匹配百分比

答案 1 :(得分:0)

您需要在排序前检查是否有任何匹配的字词:

var searchWords = query.Split(null).Distinct(StringComparer.OrdinalIgnoreCase).ToList();

var matchingItems = items.Where(s => CalculateSearchRelevance(s, searchWords) > 0);
var sortedItems = matchingItems.OrderByDescending(s => CalculateSearchRelevance(s, searchWords)).ToList();

因为你不想展示“电影中的蛋糕十六个蜡烛是用纸板制成的。”

要显示最高匹配数,您需要某种状态,例如使用Dictionary并保存所有匹配以进行进一步处理,或使用某些类来存储这些内容。

重新计算sortedItems

第一项的匹配数量

使用LINQ的Select并创建像ZiggZagg这样的匿名类型的答案,它更加优雅;)

编辑:在评论中解决您的问题

IntersectIEqualityComparer作为其中一个参数。字符串的IEqualityComparer的默认实现使用Equals,因此一种解决方案是编写自己的IEqualityComparer实现,使用Contains并根据它确定它是否相等。

class MyComparer : IEqualityComparer<string>
{
    public bool Equals(string x, string y)
    {
        return x.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0;
    }

    public int GetHashCode(string obj)
    {
        return 0;
    }
}

public static int CalculateSearchRelevance(string searchItem, IEnumerable<string> searchWords)
{
    var searchItemWords = searchItem.Split(null).ToList();
    return searchWords.Intersect(searchItemWords, new MyComparer()).Count();
}

其他方式是重写CalculateSearchRelevance,如下所示:

public static int CalculateSearchRelevance(string searchItem, IEnumerable<string> searchWords)
{
    var searchItemWords = searchItem.Split(null);
    return searchItemWords.Where(w => searchWords.Any(searchWord => w.IndexOf(searchWord, StringComparison.OrdinalIgnoreCase) >= 0)).Count();
}

通过上述实施,“迪斯尼”或“迪士尼”将与“迪士尼”和“迪士尼乐园”相匹配。我使用IndexOf而不是Contains来执行不区分大小写的操作。

请注意,如果您想要更高级的搜索引擎可能性,您可能需要查看基于Lucene构建的Lucene或Elasticsearch。你可以获得开箱即用的搜索引擎的所有功能:)许多巨头都会使用它。

https://github.com/apache/lucenenet

https://github.com/elastic/elasticsearch-net

答案 2 :(得分:0)

问题在于,即使它们不相关,您也始终包含所有结果。您只能通过检查相关性是否为&gt;来过滤项目以包含至少一个匹配项。 0

var sortedItems = items
    .Select(s => new {Text = s, Relevance = CalculateSearchRelevance(s, searchWords)})
    .Where(textWithRelevance => textWithRelevance.Relevance > 0)
    .OrderByDescending(textWithRelevance => textWithRelevance.Relevance)
    .ToList();

foreach (var sortedTextWithRelevance in sortedItems)
{
    Console.WriteLine($"Relevance: {sortedTextWithRelevance.Relevance}, Text: {sortedTextWithRelevance.Text} ");
}