【问题标题】:Sort List by occurrence of a word by LINQ C#LINQ C#按单词出现排序列表
【发布时间】:2012-07-09 09:07:21
【问题描述】:

我已将数据存储在列表中,例如

 List<SearchResult> list = new List<SearchResult>();
 SearchResult sr = new SearchResult();
 sr.Description = "sample description";
 list.Add(sr);

假设我的数据存储在描述字段中,例如

"JCB Excavator - ECU P/N: 728/35700"
"Geo Prism 1995 - ABS #16213899"
"Geo Prism 1995 - ABS #16213899"
"Geo Prism 1995 - ABS #16213899"
"Wie man BBA reman erreicht"
"this test JCB"
"Ersatz Airbags, Gurtstrammer und Auto Körper Teile"

现在我想用我的搜索词查询列表,例如 geo jcb

如果你看的话,geo这个词已经在描述字段中存储了很多次。所以我想以这样的方式对我的列表进行排序,以使搜索词中的单词找到最大的数据将首先出现。请帮我这样做。谢谢

【问题讨论】:

标签: c# linq


【解决方案1】:

您可以将string.SplitEnumerable.OrderByDescending 与匿名类型一起使用:

List<SearchResult> list = new List<SearchResult>() { 
    new SearchResult(){Description="JCB Excavator - ECU P/N: 728/35700"},
    new SearchResult(){Description="Geo Prism 1995 - ABS #16213899"},
    new SearchResult(){Description="Geo Prism 1995 - ABS #16213899"},
    new SearchResult(){Description="Geo Prism 1995 - ABS #16213899"},
    new SearchResult(){Description="Wie man BBA reman erreicht"},
    new SearchResult(){Description="this test JCB"},
    new SearchResult(){Description="Ersatz Airbags, Gurtstrammer und Auto Körper Teile"},
};

string[] searchTerms = new[]{"geo", "jcb"};
var results = 
    list.Select(sr => new { Searchresult = sr, Words = sr.Description.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) })
        .OrderByDescending(x => x.Words.Count(w => searchTerms.Contains(w.ToLower())))
        .Select(x => x.Searchresult);

【讨论】:

  • 当我像 dataGridView2.DataSource = results.ToList(); 这样绑定我的数据网格时,没有任何效果数据以相同的顺序出现.....没有发现变化。
  • @Thomas:展示你所做的。顺便说一句,在您的示例数据中,有 5 个搜索结果有 1 个匹配词,没有超过 1 个,两个有 0 个匹配。也许这就是你认为没有区别的原因。
  • 我的要求有点不同,第一次排序就像那些行将首先出现在搜索词找到最大时间的地方。结果 GEO 这个词在 5 行中找到了 3 次。 JCB 在行中找到了两次。所以所有行都以 GEO 开头,然后是 JCB。再次下一次排序就像 GEO 找到最大时间的行将排在第一位。这里 GEO & JCB 在样本中只发现一次。如果可能,请根据我的要求更改您的代码。谢谢
  • 我在这里再次发布我修改后的问题...请寻找我的要求。这里网址stackoverflow.com/questions/11393627/…
  • 当一行有两个 GEO 和两个 JCB 时,它应该在第一组还是第二组,所以在第二或第四位置(在你的sample data)?旁注:我会编辑您的问题,使其包含您第二个问题的所有信息。
【解决方案2】:

您可以使用简单的正则表达式,只需将模式中的搜索词与| 结合起来:

var re = new Regex("geo|JCB",RegexOptions.IgnoreCase);

然后计算你的描述中匹配的数量:

Console.WriteLine(re.Matches(description).Count); // Outputs '5' in your example

您可以通过以下方式订购您的清单:

searchResults.OrderByDescending(r => re.Matches(r).Count);

现场示例:http://rextester.com/MMAT58077


编辑:根据您在 cmets 中链接的新问题(希望您更新此问题的详细信息并让重复项消失)您希望对结果进行排序,以便最常见的结果显示在结果列表的前面。

为此,您可以先计算每个搜索词组的相关权重,然后使用它对结果进行排序。

Step1:通过统计每个搜索词在整个数据集中出现的总次数来计算权重:

var wordsToFind = "Geo JCB".Split();
// find number of times each search phrase is found
var weights = wordsToFind.Select( w => new { 
         Word = w, 
         Weight = list.Where(x => x.Description.Contains(w)).Count() 
    } );

对于目前这个问题中的数据,这给出了结果:

GEO: 3
JCB: 2

因此,您首先需要所有 GEO 结果,然后是 JCB。我想最好的办法是让第一个结果是最常提及GEO 的结果。

第 2 步:使用第 1 步中计算的权重对搜索结果进行排序。

var values = list.Select(x => new { 
      SearchResult = x, 
      Words = x.Description.Split(' ')
   })
   .Select(x => new { 
       SearchResult = x.SearchResult, 
       Weight = weights.Sum(w => x.Words.Contains(w.Word) ? w.Weight : 0)
   })
   .OrderByDescending(x => x.Weight)
   .Select(x => x.SearchResult);

现场示例:http://rextester.com/SLH38676

【讨论】:

  • 我在这里再次发布我修改后的问题...请寻找我的要求。这里网址stackoverflow.com/questions/11393627/…
  • @Thomas - 您需要使用更新后的信息更新 this 问题,您的新问题很快就会作为副本关闭(当我投票给关闭它)
  • 谢谢它的工作原理....但是我想要另一个排序.... GEO 行应该排在第一位,哪些行数据具有单词 GEO 最大时间。这是我的第二个要求。请你做相应的修改。非常感谢。
【解决方案3】:
List<SearchResult> list = new List<SearchResult>() 
{ 
   new SearchResult { Description = "JCB Excavator - ECU P/N: 728/35700" },
   new SearchResult { Description = "Geo Prism 1995 - ABS #16213899" },
   new SearchResult { Description = "Geo Prism 1995 - ABS #16213899" },
   new SearchResult { Description = "Geo Prism 1995 - ABS #16213899" },
   new SearchResult { Description = "Wie man BBA reman erreicht" },
   new SearchResult { Description = "this test JCB" },
   new SearchResult { Description = "Ersatz Airbags, Gurtstrammer und Auto Körper Teile" }            
   };

   var wordsToFind = "Geo JCB".Split();
   var values = list.Select(x => new { SearchResult = x, Count = x.Description.Split(' ')
                                             .Where(c => wordsToFind .Contains(c)).Count() })
                    .OrderByDescending(x => x.Count)
                    .Select(x => x.SearchResult);

【讨论】:

  • 此行给出错误 Count = x.Split(' ') 错误消息是“WindowsFormsApplication1.SearchResult 不包含 'Split' 的定义并且没有扩展方法 'Split' 接受类型的第一个参数可以找到“WindowsFormsApplication1.SearchResult”(您是否缺少 using 指令或程序集”
  • 当我像 dataGridView2.DataSource = values.ToList(); 那样绑定我的数据网格时,没有任何效果数据以相同的顺序出现.....没有发现变化。
  • @Asif: OrderByDesending => OrderByDescending
  • OrderByDescending 在我运行您的代码时并非如此,然后数据的顺序与我在此处发布的顺序相同。我的要求有点不同,第一次排序就像那些行将首先出现在搜索词找到最大时间的地方。结果 GEO 这个词在 5 行中找到了 3 次。 JCB 在行中找到了两次。所以所有行都以 GEO 开头,然后是 JCB。再次下一次排序就像 GEO 找到最大时间的行将排在第一位。这里 GEO & JCB 在样本中只发现一次。如果可能,请根据我的要求更改您的代码。谢谢
  • 我在这里再次发布我修改后的问题...请寻找我的要求。这里网址stackoverflow.com/questions/11393627/…
【解决方案4】:
var results = db.Blogs.AsEnumerable()
    .Select(sr => new
    {
        Searchresult = sr,
        Words = Regex.Split(sr.Name, @"[^\S\r\n {1,}").Union(Regex.Split(sr.Name2, @"[^\S\r\n]{1,}"))
    })
    .OrderByDescending(x => x.Words.Count(w => {
        foreach (var item in searchTerms)
        {
            if(w.ToLower().Contains(item))
            {
                return true;
            }
        }
        return false;
    }))
    .Select(x => x.Searchresult);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-22
    • 1970-01-01
    • 2021-10-23
    • 1970-01-01
    相关资源
    最近更新 更多