C#自定义列表排序
我有一个程序,可以在目录中查找所有.csv报告文件。这些报告存储在一个列表中C#自定义列表排序,c#,list,sorting,C#,List,Sorting,我有一个程序,可以在目录中查找所有.csv报告文件。这些报告存储在一个列表中 List<string> _items = new List<string>(); List_items=newlist(); 这些报告的名称如下: “month year.csv”(示例:“Avgust 2010.csv) 我想把我的报告按月分类 月份的顺序如下: 1月、2月、马雷克、4月、maj、, junij,julij,avgust,9月, 奥克托伯,11月,12月 我该怎么做 Br
List<string> _items = new List<string>();
List_items=newlist();
这些报告的名称如下:
“month year.csv”(示例:“Avgust
2010.csv)
我想把我的报告按月分类
月份的顺序如下:
1月、2月、马雷克、4月、maj、,
junij,julij,avgust,9月,
奥克托伯,11月,12月
我该怎么做
Br,Wolfy听起来您应该编写一个方法,将文件名解析为
日期时间。例如,它可以去掉扩展名,用空格分割其余部分,然后将第二部分解析为年份,并在表中查找月份名称
一旦你有了这种方法,你就可以:
_items = _items.OrderBy(FilenameToDate).ToList();
最紧凑的完整解决方案如下:
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)));
(如果您在斯洛文尼亚地区运行的系统上运行此功能,则可以使用CurrentCulture
而不是上面构造的sloveneCultureInfo
)
通过使用comparer类而不是lambda,可以进行进一步的改进,事实上,比较本身可能比上面提到的更有效,但我不会担心这一点,除非证明排序是一个瓶颈
编辑:这里的工作原理的分解
对列表进行排序有两种方便的方法(严格来说,一种是对列表进行排序,另一种是对任何IEnumerable或IQueryable进行排序)
OrderBy
接受一个计算排序键的参数,并返回一个iorderenumerable
(或者IOrderedQueryable
从现在起,我将忽略一个事实,即这也可以在IQueryable
上完成,因为原理是相同的)。这个类在大多数情况下都像一个按键排序的IEnumerable
,唯一的区别是,如果您在它上面执行后续的ThenBy
,它将按第一个键排序,因此允许您进行多级排序
因此,在代码中:
var x = _items.OrderBy(SomeMethod);
SomeMethod
将返回可排序的值,在此基础上,x将被赋予一个相应排序的IORDerenumerable
。例如,如果SomeMethod
获取一个字符串并返回该字符串的长度,那么我们可以使用它按长度对字符串列表进行排序
如果我们要遍历_项,那么这就是我们的工作完成了。如果我们想要一个新的列表,那么我们可以对结果调用Tolist()
,这将返回这样一个列表
另一种只对列表有效的方法是调用Sort()
。有一个无参数表单,它只根据所讨论类型的默认比较(如果有)进行排序,一个表单采用IComparer
对象(在大多数情况下涉及的内容比必要的多,但有时非常有用),一个表单采用委托或lambda(严格来说,它需要一个委托,但我们可以使用lambda来创建该委托)
此委托必须接收列表项类型的两个值,如果第一个值排序早于后一个值,则返回负数;如果它们相等,则返回零;否则返回正数
Sort()
更改列表的完成时间。这会提高效率,但如果您还需要保留原始排序顺序,则显然是灾难性的
好的,到目前为止,我们有两种方法对列表进行排序。这两种方法都需要将文件名转换为可以排序的内容
由于文件名与日期相关,并且日期已经可以排序,因此最明智的方法是获取这些日期
我们不能直接从文件名创建日期,因为Avgust 2010不是一个日期,只是一个月-年值,BCL中没有一个月-年类(可能在其他库中有,但我们不要镀金百合花)
但是,每个月都有第一天,因此我们可以从与文件名连接的“01”创建一个有效日期。因此,我们的第一步是说,我们将对“01”+fn
采取行动,其中fn
是文件名
这为我们提供了字符串的形式,例如“01 Avgust 2010.csv”
。通过检查日期解析的工作原理,我们知道可以使用dd
表示两位数的日期,MMMM
表示相关语言中月份的全名(本例中为斯洛文尼亚语)和全年的yyyy
。我们只需要添加\.\c\s\v
,这意味着它后面会有一个我们不解析的“.csv”,我们已经设置好了。因此,我们可以将文件名转换为其月份的第一个日期时间。ParseExact(“01”+fn,@“dd MMMM yyyy\。\c\s\v”,新文化(“sl SI”))
其中fn是文件名。要使其成为lambda表达式,我们使用fn=>DateTime.ParseExact(“01”+fn,@“dd-MMMM-yyyy\。\c\s\v”,新区域性(“sl-SI”)
第一种方法现在已经完成,我们调用\u items.OrderBy(fn=>DateTime.ParseExact(“01”+fn,@“dd-MMMM-yyyy\。\c\s\v”,新区域性(“sl-SI”))
,并且依赖于OrderBy
知道如何排序DateTime
值
对于第二种方法,我们需要获取两个值并自己进行比较。我们可以通过调用ParseExact
返回的DateTime
上的CompareTo()
来实现这一点
最后,我们将
CultureInfo
结构移出,以初始化名为sloveneCultureInfo
的变量,以避免对基本相同对象的多个创建进行浪费性调用。@generic否,这是正确的。目的是将委托传递到OrderBy,以便在排序过程中调用它,而不是将其称为传递OrderBy的未定义结果