我有一个程序可以在目录中找到所有.csv报告文件。此报告存储在列表
中List<string> _items = new List<string>();
此报告的名称如下:
“month year.csv”(例如:“Avgust 2010.csv“)
我想按月对报告进行排序。
月份按此顺序排列:
januar,februar,marec,4月,maj, junij,julij,avgust,9月, 十月,十二月,十月,
我该怎么做?
Br,Wolfy
答案 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
的变量,以避免浪费调用基本上相同对象的多个创建。