2

我有一个List<Locations>将被过滤以产生一组与搜索词相关的结果。

目前,我通过以下过滤尝试了这些“搜索结果”:

return locations.Where(o => o.Name.Contains(term)).Take(10).ToList();

问题

如果我输入“Chester”作为搜索词,我将永远不会看到“Chester”项目,尽管它存在于locations列表中。原因是列表中有 10 个或更多其他项目的名称中包含字符串“Chester”(Manchester、Dorchester 等)。

我如何使用 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 并结合两个列表。

4

5 回答 5

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();
于 2013-10-24T14:59:45.900 回答
4

我创建了一个新的 github 项目,它使用表达式树在任意数量的属性中搜索文本

它还有一个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/

于 2014-01-21T17:50:22.360 回答
2

Raphaël 的解决方案将起作用,但如果您说搜索 Warwick,您可能会发现如果 Warwickshire 也是一个可能的位置,它可能不会将 Warwick 放在列表的顶部,使用分数您还可以使用更多匹配方法无限扩展它,以及调整分值以优化您的搜索顺序

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(Match).Take(10);
于 2013-10-24T15:39:36.633 回答
1

所有解决方案都将起作用,但更好的分数可以获得更容易,如下所示

return locations.Where(o => o.Name.Contains(term))
            .OrderBy(m => m.Name.IndexOf(term))
            .Take(10)
            .ToList();

因此,每个名称包含最接近开始的术语,首先显示。

于 2015-10-20T11:30:13.647 回答
0

那这个呢?

return locations.Where(o => o.Name.Contains(term))
                .OrderBy(m => m.Length)
                .Take(10)
                .ToList();
于 2017-02-24T20:37:45.473 回答