C#自定义列表排序

时间:2010-10-04 08:23:02

标签: c# list sorting

我有一个程序可以在目录中找到所有.csv报告文件。此报告存储在列表

List<string> _items = new List<string>();

此报告的名称如下:

  

“month year.csv”(例如:“Avgust   2010.csv“)

我想按月对报告进行排序。

月份按此顺序排列:

  

januar,februar,marec,4月,maj,   junij,julij,avgust,9月,   十月,十二月,十月,

我该怎么做?

Br,Wolfy

2 个答案:

答案 0 :(得分:4)

听起来你应该编写一个方法来将文件名解析为DateTime。例如,它可能会取消扩展,将其余部分按空格分割,然后将第二部分解析为年份,并在表格中查找月份名称。

一旦你有了这种方法,你就可以这样做:

_items = _items.OrderBy(FilenameToDate).ToList();

答案 1 :(得分:3)

最紧凑的完整解决方案如下:

CultureInfo sloveneCultureInfo = new CultureInfo("sl-SI");
_items = _items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", sloveneCultureInfo)).ToList();

以上将此列表分配给自己。如果您打算这样做,那么通过就地排序,您可以获得相对较少的代码复杂性增加的性能:

CultureInfo sloveneCultureInfo = new CultureInfo("sl-SI");
_items.Sort((x, y) => DateTime.ParseExact("01 " + x, @"dd MMMM yyyy\.\c\s\v",sloveneCultureInfo).CompareTo(DateTime.ParseExact("01 " + y, @"dd MMMM yyyy\.\c\s\v", sloveneCultureInfo)));

(如果您在以Slovene语言环境运行的系统上运行此功能,那么您可以使用CurrentCulture而不是上面构建的sloveneCultureInfo

通过使用比较器类而不是lambda可以进一步改进,实际上比较本身可能比上面更有效,但我不担心这个,除非事实被证明是瓶颈。

编辑:分解这里的作用。

有两种方便的方法可以对列表进行排序(严格来说,一种是对列表进行排序,另一种是对任何IEnumerable或IQueryable进行排序)。

OrderBy接受一个计算排序键的参数,并从现在开始返回IOrderedEnumerable<T>(或IOrderedQueryable<T>我将忽略这样的事实,即可以在{ {1}},因为原则是相同的)。这个类在大多数情况下都像IQueryable那样按键排序,唯一的区别是如果你对它进行后续的IEnumerable<T>,它会按第一个键排序,因此,您可以进行多阶段排序。

所以,在代码中:

ThenBy

var x = _items.OrderBy(SomeMethod); 将返回可以排序的值,并且在此基础上,x将被赋予相应排序的SomeMethod。例如,如果IOrderedEnumerable<T>接受一个字符串并返回字符串的长度,那么我们可以使用它来按字符串长度排序。

如果我们要迭代_items,那么这就完成了我们的工作。如果我们想要一个新的列表,那么我们可以在结果上调用SomeMethod,这将返回这样的列表。

另一种仅适用于列表的方法是调用Tolist()。有一个无参数的表单,只根据相关类型的默认比较(如果有的话)进行排序,一个采用Sort()对象的表单(在大多数情况下涉及的比较多,但有时非常有用)以及一个带委托或lambda的表单(严格来说它需要一个委托,但我们可以使用lambda来创建该委托)。

此委托必须接收列表项类型的两个值,如果第一个应该排在后者之前,则返回一个负数,如果它们是等价的则为零,否则返回正数。

IComparer<T>更改其完成的列表。这样可以提高效率,但如果您需要保留原始排序顺序,则显然是灾难性的。

好的,到目前为止,我们有两种方法对列表进行排序。他们俩都需要一种方法将你的文件名变成可以分类的东西。

由于文件名与日期有关,并且由于日期已经可以排序,因此最明智的方法是获取这些日期。

我们无法直接从文件名创建日期,因为 Avgust 2010 不是日期,只是月份值,而且BCL中没有月份年级(可能在其他图书馆,但让我们不是镀金百合花。)

但是,每个月都有第一天,因此我们可以创建一个与文件名连接的“01”的有效日期。因此,我们的第一步是说我们将在Sort()上采取行动,其中"01 " + fn是文件名。

这为我们提供了例如fn。检查日期解析的工作原理,我们知道我们可以使用"01 Avgust 2010.csv"表示两位数的日期,dd表示相关语言的月份全名(本例中为斯洛文尼亚语)和{{1}全年。我们只需要添加MMMM来表示它将跟随我们不解析的“.csv”,并且我们已经设置好了。因此,我们可以使用yyyy将文件名转换为其当月的第一个文件名,其中fn是文件名。为了使它成为lambda表达式,我们使用\.\c\s\v

第一种方法现已完成,我们致电DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI"))并依赖fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI"))了解如何对_items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI")))值进行排序。

对于第二种方法,我们需要采用两个值并自己进行比较。我们可以通过致电OrderBy返回的DateTime上的CompareTo()来完成此操作。

最后,我们将DateTime构造移出以初始化一个名为ParseExact的变量,以避免浪费调用基本上相同对象的多个创建。