如何简化在字符串中搜索关键字并按相关性排序的 LINQ 查询?

Posted

技术标签:

【中文标题】如何简化在字符串中搜索关键字并按相关性排序的 LINQ 查询?【英文标题】:How can I simplify this LINQ query that searches for keywords in strings and orders them by relevance? 【发布时间】:2018-11-24 03:56:02 【问题描述】:

假设我有一些MyObjects,它们每个都有一个Description 属性。我有一个关键字列表,我想用它来搜索MyObject 列表。我想按每个Description 包含的关键字数量对它们进行降序排列。

示例输入(仅显示Description 属性,注意初始顺序):

"Foo Bar"
"Foo Boo"
"Bar Bar"

示例关键字:

"Boo", "Foo"

示例输出(仅显示Description 属性,注意最终顺序):

"Foo Boo" (matches 2 keywords)
"Foo Bar" (matches 1 keyword)

“Bar”“Bar”不在结果中,因为它匹配 0 个关键字。

我目前正在使用这个非常复杂的方法链:

return keywords.SelectMany(
    x => MyObjects.Where(y => y.Description.ToLowerInvariant().Contains(x.ToLowerInvariant()))
    )
    .GroupBy(x => x)
    .OrderByDescending(x => x.Count())
    .Select(x => x.Key).ToList();

如您所见,我首先选择keywords。我认为,作为代码的读者,您希望首先看到在MyObjects 上进行一些转换。通常,当我编写 LINQ 时,我会尝试在脑海中想象操作会是什么样子。看到正在转换的关键字只是感觉违反直觉。我也不喜欢SelectMany 中的嵌套查询,因为它使查询语法看起来很丑:

var query = from keyword in keywords
            from matchedObjects in (from obj in MyObjects where obj.Description.ToLowerInvariant().Contains(keyword.ToLowerInvariant()) select obj)
            group matchedObjects by matchedObjects into sameObjects
            orderby sameObjects.Count() descending
            select sameObjects.Key;
return query.ToList();

如何改进 LINQ 查询?理想情况下:

没有嵌套查询 以MyObjects.SomeLINQOperation... 开头,而不是keywords

我希望有一种更简单/更直观的方法,因为这似乎是一件微不足道的事情,但如果提供解释,我也接受没有更简单的方法。

【问题讨论】:

您认为什么是“改进”?更快的代码?短代码?更少的内存消耗代码?因为这个问题是非常基于选项的,并且吸引了许多不同的答案来争取不同的测量结果。除了这个关于工作代码的问题应该去codereview.stackechange.com @HimBromBeere 看到两个要点了吗?这就是我想要的,与速度无关。我不认为这是基于意见的,因为这两点非常客观。 【参考方案1】:

results = myObjects.OrderByDescending(myObject => keywords.Where(keyword => myObject.Description.Contains(keyword)).Count());

给你你需要的东西?

编辑:

var temp = myObjects.Where(myObject => keywords.Any(keyword => myObject.Description.Contains(keyword)))
            .OrderByDescending(myObject => keywords.Where(keyword => myObject.Description.Contains(keyword)).Count());

不确定这算不算“更好”。

【讨论】:

错过了关于过滤掉没有关键字的 MyObjects 的部分。编辑了我的答案。 这满足了我的一个愿望 +1 :) 如果没有其他人发布更好的东西或解释为什么不能同时满足两者,我会接受。【参考方案2】:

试试看:

    var objects = new[]
                    new MyObjectDescription = "Foo Bar",
                    new MyObjectDescription = "Foo Boo",
                    new MyObjectDescription = "Foo Bee",
                    new MyObjectDescription = "Bar Bee",
                    new MyObjectDescription = "Boo Bee",
                ;
                var keywords = new[]  "Foo", "Bar" ;
                var results = objects
                    .GroupBy(x => keywords.Where(
                                          keyword => x.Description.Contains(keyword) 
                                          ).Count()
                    )
                    .Where(x => x.Key > 0) // discard no matches
//                    .OrderByDescending(x => x.Count()) // order by mathing objects count
                    .OrderByDescending(x => x.Key)
//                   .ToDictionary(x => x.Key, x => x.ToArray())
                     .Select(x => new Count = x.Key, Objects = x.ToArray()).ToList(); // or create anonymous type
                    ;

它按匹配计数对对象进行分组,丢弃没有匹配并将最多匹配放在顶部

【讨论】:

Tbh 我不需要匹配多少关键字,所以SelectMany(x => x) 可能会起作用,对吧? @Sweeper 是的,应该

以上是关于如何简化在字符串中搜索关键字并按相关性排序的 LINQ 查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何在laravel中检索有限数量的相关模型并按相关模型对集合进行排序?

如何在 SQL Server 中连接字符串,并按不同的列排序/排序?

百度搜索引擎是如何排序的

如何根据 sql 中的匹配数针对关键字数组和排序结果搜索字符串?

查询自定义帖子类型并按自定义帖子类型排序

Android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能