[英]How do I remove rows from a DataTable up to a certain date?
我在过滤 DataTable 时遇到问题,比如DtFromExcel
。 DataTable 没有 header 行,它以实际数据行开头,如下所示。
1 | 05/01/2020 Fri | ABC | XYZ | ...
2 | 05/01/2020 Fri | AAA | WKV | ...
3 | 05/02/2020 Sat | BCD | OPQ | ...
4 | 05/03/2020 Sun | CDE | RST | ...
5 | 05/03/2020 Sun | EFA | FAY | ...
6 | 05/03/2020 Sun | AXG | EAS | ...
7 | 05/04/2020 Mon | DEF | LMN | ...
8 | 05/04/2020 Mon | SXA | YTR | ...
9 | 05/05/2020 Tue | DAF | AAG | ...
第二列包含带有一些额外字符串(星期几)的特定日期,这些行按此日期列排序。 可以有多个具有相同日期的行。
现在,我想删除日期列包含某个日期的行以及之前的任何行。 例如,如果某个日期是05/04/2020
,那么我需要删除第 8 行之前的所有行,以便剩余的 DataTable 看起来像
9 | 05/05/2020 Tue | DAF | AAG | ...
我的问题是,首先我不知道如何过滤没有列名的 DataTable。 我想过在不覆盖第一个实际数据行的情况下分配 header 行,但似乎这是很多工作,只是为了过滤。 其次,我不确定如何使用这些条件((a)第二列包含某个日期AND
以及(b)任何包含该日期之前的日期的行)。
private void DeleteRows(DateTime certainDate){
DataRow[] targetRowsToDelete = dtFromExcel.Select(/* Not sure what to put in here */);
foreach (DataRow row in targetRowsToDelete)
{
if (Convert.ToDateTime(row[1].ToString().Split(c" ")[0]) <= certainDate)
DtFromExcel.Rows.Remove(row);
}
}
我不想遍历整个 DataTable,因为这个过程经常发生在我的程序中。
如果您使用空构造函数创建一个没有名称的DataColumn
,文档说明...
创建时,
DataColumn
object 没有默认的ColumnName
或Caption
。 当您将其添加到DataColumnCollection
时,如果尚未为ColumnName
分配名称,则将生成默认名称("Column1"
、"Column2"
等)。
...因此创建和加载这样的DataTable
...
const string Input = @"1 | 05/01/2020 Fri | ABC | XYZ | ...
2 | 05/01/2020 Fri | AAA | WKV | ...
3 | 05/02/2020 Sat | BCD | OPQ | ...
4 | 05/03/2020 Sun | CDE | RST | ...
5 | 05/03/2020 Sun | EFA | FAY | ...
6 | 05/03/2020 Sun | AXG | EAS | ...
7 | 05/04/2020 Mon | DEF | LMN | ...
8 | 05/04/2020 Mon | SXA | YTR | ...
9 | 05/05/2020 Tue | DAF | AAG | ...";
DtFromExcel = new DataTable();
for (int i = 0; i < 5; i++)
{
DataColumn column = new DataColumn();
Console.WriteLine($"Column {i} has ColumnName \"{column.ColumnName}\"");
DtFromExcel.Columns.Add(column);
Console.WriteLine($"Column {i} has ColumnName \"{column.ColumnName}\"");
}
foreach (string line in Input.Split("\r\n"))
{
string[] fields = line.Split(" | ");
DtFromExcel.Rows.Add(fields);
}
...生产此 output...
Column 0 has ColumnName "" Column 0 has ColumnName "Column1" Column 1 has ColumnName "" Column 1 has ColumnName "Column2" Column 2 has ColumnName "" Column 2 has ColumnName "Column3" Column 3 has ColumnName "" Column 3 has ColumnName "Column4" Column 4 has ColumnName "" Column 4 has ColumnName "Column5"
...因此您始终可以使用这些默认名称。 此外,仅仅因为您的输入数据未指定列/字段名称并不意味着在将其加载到DataTable
后您不能这样做...
DtFromExcel.Columns[1].ColumnName = "MyDateColumn";
无论哪种方式,您都会有一个已知名称,您可以通过该名称引用该列。
至于您关于不想“循环遍历整个DataTable
”的评论,尚不清楚您的意思是因为附加代码还是性能影响,但到后一点,即使您没有明确循环并测试每个DataRow
, Select()
将. 关于这一点,由于您说行是按日期排序的,因此您可以利用LINQ在找到搜索范围之外的日期后立即停止扫描行...
private static DateTime GetRowDate(DataRow row) => DateTime.ParseExact(
(string) row["MyDateColumn"], "MM/dd/yyyy ddd", null
);
private void DeleteRows(DateTime maxDate)
{
DataRow[] rowsToRemove = DtFromExcel.AsEnumerable()
.TakeWhile(row => GetRowDate(row) <= maxDate)
.ToArray();// Required to prevent "Collection was modified" exception in foreach below
foreach (DataRow row in rowsToRemove)
DtFromExcel.Rows.Remove(row);
}
如果您的行不能保证按日期排序,那么您可以将Where()
替换为TakeWhile()
并且它的工作方式相同。
至于您使用DateTable.Select()
的原始请求,我不确定这是否可行,因为您的日期似乎存储为string
,而不是DateTime
,在您的DataColumn
中。 我看到表达式语法支持可以在String
和DateTime
之间转换的CONVERT()
function ,但我无法想象它会比 LINQ 更具性能或可读性,所以除非你绝对必须这样做,否则我不会追求它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.