2009-08-11 55 views
3

我在供应商的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; 
} 

回答

3

如何开发计算Levenshtein距离StringComparer - 精确匹配这应该是零 - 在这个排序的结果升序。这样你首先得到你的完全匹配,并且其余的结果按照搜索字符串的相似性(至少一个度量)排序。

var list = vendors.Where(v => v.Name.ToUpper().Contains(vendorNameSearch)) 
        .OrderBy(v => ComputeLevenshtein(v.Name.ToUpper(), 
                vendorNameSearch)); 

或者你也可以做出与精确匹配是int.MinValue和所有其他值是的CompareTo的结果()定购的东西比较器。这也将首先命令完全匹配。

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(); 
1

排序中的字符串长度的绝对差。确切的比赛将是唯一一个具有相同的长度,而其他人都将大于0(绝对差异= 0。):

var list = vendors.Where(v => v.Name.ToUpper().Contains(vendorNameSearch)) 
        .OrderBy(v => Math.Abs(v.Name.ToUpper().Length - vendorNameSearch.Length))); 

这将是一个任意否则排序,但它满足你的主要目标。

但是,当然,如果您完成这项工作,那么像您的原始代码这样的两步解决方案没有任何问题。

3

首先,我喜欢tvanfosson从可用性角度回答的方法。

否则,您可以使用复合排序达到你想要的行为:

vendors.Where(v => v.Name.ToUpper().Contains(vendorNameSearch)) 
     .OrderBy(v => !string.Equals(v.Name, vendorNameSearch)) 
     .ThenBy(v => v.Name) 
     .ToList(); 

排序方式精确匹配比较的布尔结果将确保精确匹配列在第一位。由于布尔值排序的方式,具有精确名称匹配的供应商应具有值为false。您也可以使用OrderByDescending来实现此目的,而不是颠倒字符串相等。

相关问题