我想按单词对c#列表进行排序。假设我有一个c#对象列表,其中包含以下单词:
[{id:1, name: "ABC"},
{id:2, name: "XXX"},
{id:3, name: "Mille"},
{id:4, name: "YYY"},
{id:5, name: "Mill",
{id:6, name: "Millen"},
{id:7, name: "OOO"},
{id:8, name: "GGGG"},
{id:9, name: null},
{id:10, name: "XXX"},
{id:11, name: "mil"}]
如果用户传递Mil
作为搜索键,我想返回所有以搜索键&然后是所有不符合条件的单词&按字母顺序排序。
我能想到的最简单的方法是在结果集上运行for循环,将所有以search key开头的单词放入一个列表中,并将重命名单词放入另一个列表中。对第二个列表进行排序,然后将两个列表组合以返回结果。
我想知道是否有一种更聪明或内置的方法来获得所需的结果。
当然!您将根据是否存在匹配进行排序,然后按名称排序,如下所示:
var results = objects.OrderByDescending(o => o.Name.StartsWith(searchKey))
.ThenBy(o => o.Name);
注意,false
在排序中出现在true
之前,所以您需要使用OrderByDescending
。
正如AlexD指出的,名称可以为空。你得决定怎么处理这件事。最简单的方法是使用o.Name?.StartsWith(searchKey) ?? false
,但您必须根据自己的需要做出决定。而且,并不是所有的Linq场景都支持null传播(我想到了Linq To Entities)。
应该这样做,但可能有一个更快的方法,也许使用GroupBy
的某种方式。
var sorted = collection
.Where(x => x.Name.StartsWith(criteria))
.OrderBy(x => x.Name)
.Concat(collection
.Where(x => !x.Name.StartsWith(criteria))
.OrderBy(x => x.Name))
您可以这样尝试GroupBy
:
var sorted = collection
.GroupBy(item => item.Name.StartsWith(criteria))
.OrderByDescending(chunk => chunk.Key)
.SelectMany(chunk => chunk
.OrderBy(item => item.Name));
- 将物品分为两组(符合和不符合标准)
- 整体排序(第一个满足)
- 在每组内订购物品
- 最后合并项目
没有c#特定的方法来解决这个问题,但听起来你真的在寻找算法设计指南。
你应该先对列表排序。如果这是一个静态列表,您应该始终保持它的排序。如果列表很大,你可以考虑使用不同的数据结构(二叉搜索树,跳跃表等),这对这种情况更优化。
排序后,查找匹配元素就变成了简单的二分查找。将匹配的元素移动到结果集的开头,然后返回。
在select中添加一个匹配的指示符,然后对其进行排序:
void Main()
{
word[] Words = new word[11]
{new word {id=1, name= "ABC"},
new word {id=2, name= "XXX"},
new word {id=3, name= "Mille"},
new word {id=4, name= "YYY"},
new word {id=5, name= "Mill"},
new word {id=6, name= "Millen"},
new word {id=7, name= "OOO"},
new word {id=8, name= "GGGG"},
new word {id=9, name= null},
new word {id=10, name= "XXX"},
new word {id=11, name= "mil"}};
var target = "mil";
var comparison = StringComparison.InvariantCultureIgnoreCase;
var q = (from w in Words
where w.name != null
select new {
Match = w.name.StartsWith(target, comparison)?1:2,
name = w.name})
.OrderBy(w=>w.Match).ThenBy(w=>w.name);
q.Dump();
}
public struct word
{
public int id;
public string name;
}
这可能并不容易,但您可以创建一个实现IComparable Interface的类,并拥有一个由CompareTo使用的属性Mil。
那么你可以直接调用List.Sort()。你可以传递一个比较器给list。sort。
这可能是最有效的,你可以就地排序,而不是产生一个新的List。
该方法平均为O(n log n)次运算,其中n为Count;在最坏的情况下,它是一个O(n ^ 2)的操作。
public int CompareTo(object obj)
{
if (obj == null) return 1;
Temperature otherTemperature = obj as Temperature;
if (otherTemperature != null)
{
if(string.IsNullOrEmpty(Mil)
return this.Name.CompareTo(otherTemperature.Name);
else if(this.Name.StartsWith(Mill) && otherTemperature.Name.StartsWith(Mill)
return this.Name.CompareTo(otherTemperature.Name);
else if(!this.Name.StartsWith(Mill) && !otherTemperature.Name.StartsWith(Mill)
return this.Name.CompareTo(otherTemperature.Name);
else if(this.Name.StartsWith(Mill))
return 1;
else
return 0;
}
else
throw new ArgumentException("Object is not a Temperature");
}
您将需要添加您希望null Name如何排序
首先创建一个匹配的单词列表,排序。然后将所有未被添加到第一个列表的单词添加到该列表中,也进行了排序。
public IEnumerable<Word> GetSortedByMatches(string keyword, Word[] words)
{
var result = new List<Word>(words.Where(word => word.Name.StartsWith(keyword))
.OrderBy(word => word.Name));
result.AddRange(words.Except(result).OrderBy(word => word.Name));
return result;
}
一些注释建议它应该是不区分大小写的。那就是
public IEnumerable<Word> GetSortedByMatches(string keyword, Word[] words)
{
var result = new List<Word>(
words.Where(word => word.Name.StartsWith(keyword, true)) //<-- ignoreCase
.OrderBy(word => word.Name));
result.AddRange(words.Except(result).OrderBy(word => word.Name));
return result;
}