【问题标题】:Most precise result in my search app我的搜索应用程序中最精确的结果
【发布时间】:2018-04-02 11:58:10
【问题描述】:

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

  • 沃尔特·迪斯尼 (Walt Disney) 拒绝让阿尔弗雷德·希区柯克 (Alfred Hitchcock) 在 1960 年代初在迪斯尼乐园拍摄电影,因为他制作了“那部令人作呕的电影《惊魂记》。
  • 狮子王中的彭巴是迪士尼电影中第一个放屁的角色。
  • 沃尔特·迪斯尼 (Walt Disney) 向白雪公主和七个小矮人 (Snow White and the Seven Dwarfs) 中的动画师支付了 5 美元,用于制作电影最终版本中的任何插科打诨。
  • 电影《十六支蜡烛》中的蛋糕是用硬纸板做的。

等等

我想在我的列表框中只显示这些包含在搜索框中输入的任何单词的句子。例如:当我输入“迪士尼七个小矮人”时,它应该显示“华特迪士尼在白雪公主和七个小矮人中支付了动画师的费用任何进入电影最终版本的恶作剧都需要 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();

【问题讨论】:

  • 好的 .. 我们知道你想要什么。但是我们需要查看您编写的代码以及您遇到的问题?
  • 所以这些是要求。您遇到的问题是什么?
  • 更新 :) 下一步是什么?
  • 几乎一切正常,但是当我输入 f.e 这个:“这是美好的一天”时,它仍然显示我文件中的所有句子。它应该显示包含任何输入单词的句子。
  • 要实现模糊搜索吗?如果是,请检查这个问题 - stackoverflow.com/questions/24948750/…

标签: c# oop


【解决方案1】:

排序前需要检查是否有匹配的单词:

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,因此一种解决方案是编写您自己的使用 ContainsIEqualityComparer 实现,并据此决定它是否相等。

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();
}

通过上述实现,“disne”或“disney”将匹配“Disney”和“Disneyland”。我使用 IndexOf 而不是 Contains 来执行不区分大小写的操作。

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

https://github.com/apache/lucenenet

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

【讨论】:

  • 但我想在输入字母时显示最精确的结果。示例:“disne”应显示包含这些单词/字母的句子。目前,仅当我输入整个单词时才会显示结果。
  • 哦,那等等。我会更新我的答案。您似乎想要某种自动完成/提前输入。
  • 好的,我等着。 ;)
  • @FrankLeeMydear 我正在用几个解决方案来回答,所以你可以选择
  • 完美!一切正常!太感谢了。 :)
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    问题是您总是包含所有结果,即使它们不相关。您可以仅通过检查相关性是否 > 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} ");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-08
      • 1970-01-01
      • 2016-01-07
      • 2010-12-08
      • 1970-01-01
      • 1970-01-01
      • 2012-02-27
      • 1970-01-01
      相关资源
      最近更新 更多