我有一个List<Locations>
会被过滤,以产生一组与搜索字词相关的结果。
目前,我通过使用以下内容进行过滤来尝试这些“搜索结果”:
return locations.Where(o => o.Name.Contains(term)).Take(10).ToList();
问题
如果我输入'切斯特'作为搜索词,我将永远不会看到“切斯特”项目,尽管它存在于locations
列表中。原因是列表中有10个或更多其他项目,其名称中包含字符串“Chester”(曼彻斯特,多切斯特等)。
如何使用LINQ首先获取以搜索词开头的结果?
我到目前为止
var startsWithList = list.Where(o => o.Name.StartsWith(term)).Take(10).ToList();
var containsList = list.Where(o => o.Name.StartsWith(term) && !startsWithList.Contains(o)).Take(10 - startsWithList.Count).ToList();
return startsWithList.AddRange(containsList);
我根本不喜欢上面的代码。我觉得这应该在一个Where
中实现,而不是执行两个Where和Take并将两个列表组合起来。
答案 0 :(得分:8)
在Take之前按顺序递增,为以term开头的项目设置较低的值。
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.StartsWith(term) ? 0 : 1)
//or OrderByDescending(m => m.Name.StartsWith(term))
.Take(10)
.ToList();
适应了MikeT的改进(StartsWith之前的完全匹配),你可以做到
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.StartsWith(term)
? (m.Name == term ? 0 : 1)
: 2)
.Take(10)
.ToList();
答案 1 :(得分:4)
我创建了一个new github project,它使用表达式树来搜索任意数量的属性
中的文本它还有一个RankedSearch()
方法,它返回匹配的项目,包含每条记录的点击次数,这意味着您可以执行以下操作:
return locations.RankedSearch(term, l => l.Name)
.OrderByDescending(x => x.Hits)
.Take(10)
.ToList();
如果您希望可以搜索多个属性
return locations.RankedSearch(term, l => l.Name, l => l.City)
...或多个字词,
return locations.RankedSearch(l => l.Name, term, "AnotherTerm" )
...或多个属性和多个术语
return locations.RankedSearch(new[]{term, "AnotherTerm"},
l => l.Name,
l => l.City)
查看此帖子,了解有关SQL生成和其他用法的更多信息: http://jnye.co/Posts/27/searchextensions-multiple-property-search-support-with-ranking-in-c-sharp
您可以将其作为nuget包下载到: https://www.nuget.org/packages/NinjaNye.SearchExtensions/
答案 2 :(得分:1)
return locations.Select(l => New {SearchResult=l,
Score=(L.Name == Term ?
100 :
l.Name.StartsWith(term) ?
10 :
l.Name.Contains(term) ?
1 :
0
)})
.OrderByDescending(r=>r.Score)
.Take(10)
.Select(r => r.SearchResult);
请注意我可能会通过制作一个Match方法并在那里执行逻辑而不是像上面那样在linq中执行此操作,因此它只是
return locations.OrderByDescending(r=>Match(r)).Take(10);
答案 3 :(得分:1)
所有解决方案都能正常运行,但更好的分数可以更容易获得,如下所示
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.IndexOf(term))
.Take(10)
.ToList();
因此,每个名称包含 term ,最接近开始,首先显示。
答案 4 :(得分:0)
这个怎么样?
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Length)
.Take(10)
.ToList();