我在供应商的C#中有一个列表,它们都有一个Name属性。我想允许用户通过搜索名称来过滤该列表。过滤字符串可以是部分匹配或完全匹配。但是,如果结果列表包含完全匹配,则它应该在列表中的位置为零,之后将包含所有部分匹配。
我可以很容易地使用linq和lambdas获取子列表但是我不得不求助于创建第二个列表,如果存在完全匹配,添加它,然后添加其余的匹配而不使用确切的一个。感觉不够优雅。有没有更简单的方法?我当前的代码(从内存完成,因此可能无法编译):
List<Vendor> temp = vendors.Where(v => v.Name.ToUpper().Contains(vendorNameSearch)).ToList();
Vendor exactMatch = vendors.Single(v => v.Name.ToUpper().Equals(vendorNameSearch));
if(null == exactMatch){return temp;}
else
{
List<Vendor> temp1 = new List<Vendor>();
temp1.Add(exactMatch);
temp1.AddRange(temp.Remove(exactMatch));
return temp1;
}
答案 0 :(得分:3)
开发一个计算Levenshtein距离的StringComparer如何 - 对于精确匹配应该为零 - 并按升序对结果进行排序。这样,您首先得到完全匹配,其余结果按搜索字符串的相似性(至少一个度量)排序。
var list = vendors.Where( v => v.Name.ToUpper().Contains( vendorNameSearch ) )
.OrderBy( v => ComputeLevenshtein( v.Name.ToUpper(),
vendorNameSearch ) );
或者你可以制作一个比较器来命令具有完全匹配的东西为int.MinValue,而所有其他值都是CompareTo()的结果。这也将首先排序完全匹配。
答案 1 :(得分:3)
首先,我喜欢tvanfosson从可用性角度回答的方法。
否则,您可以使用复合排序实现所需的行为:
vendors.Where(v => v.Name.ToUpper().Contains(vendorNameSearch))
.OrderBy(v => !string.Equals(v.Name, vendorNameSearch))
.ThenBy(v => v.Name)
.ToList();
按照完全匹配比较的布尔结果排序将确保首先列出完全匹配。由于布尔值的排序方式,具有精确名称匹配的供应商应具有值false。您也可以使用OrderByDescending实现此目的,而不是反转字符串相等。
答案 2 :(得分:1)
我不知道这有多高效,但这是另一种选择,
List<String> strings = new List<String> {"Cat","Dog","Pear","Apple","Catalog"};
var results = (from st in strings
where st == "Cat"
select new {Priority = 1,st}).Union(
from st in strings
where st.Contains("Cat")
select new {Priority = 2, st}).OrderBy(x => x.Priority).Select(x=> x.st).Distinct();
答案 3 :(得分:1)
按字符串长度的绝对差异排序。完全匹配将是唯一具有相同长度的匹配(绝对差异= 0),而其他匹配将全部大于0:
var list = vendors.Where(v => v.Name.ToUpper().Contains(vendorNameSearch))
.OrderBy(v => Math.Abs(v.Name.ToUpper().Length - vendorNameSearch.Length)));
这将是一个任意的顺序,但它实现了你的主要目标。
但是,当然,像原始代码这样的两步解决方案没有任何问题,如果能够完成这项工作的话。