我如何简化这个在字符串中搜索关键字并按相关性排序的 LINQ 查询?
How can I simplify this LINQ query that searches for keywords in strings and orders them by relevance?
假设我有一些 MyObject
,每个人都有一个 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
。我认为作为代码的 reader,您会希望看到先对 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
。
我希望有一种 easier/more 直观的方法,因为这似乎是一件微不足道的事情,但我也承认,如果提供解释,没有更简单的方法。
是否
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());
不确定这是否算作 'better'。
试试看:
var objects = new[]{
new MyObject{Description = "Foo Bar"},
new MyObject{Description = "Foo Boo"},
new MyObject{Description = "Foo Bee"},
new MyObject{Description = "Bar Bee"},
new MyObject{Description = "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
;
它按匹配计数对对象进行分组,不丢弃任何匹配项并将最多匹配项放在顶部
假设我有一些 MyObject
,每个人都有一个 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
。我认为作为代码的 reader,您会希望看到先对 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
。
我希望有一种 easier/more 直观的方法,因为这似乎是一件微不足道的事情,但我也承认,如果提供解释,没有更简单的方法。
是否
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());
不确定这是否算作 'better'。
试试看:
var objects = new[]{
new MyObject{Description = "Foo Bar"},
new MyObject{Description = "Foo Boo"},
new MyObject{Description = "Foo Bee"},
new MyObject{Description = "Bar Bee"},
new MyObject{Description = "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
;
它按匹配计数对对象进行分组,不丢弃任何匹配项并将最多匹配项放在顶部