繁体   English   中英

如何从 DataTable 中删除直到某个日期的行?

[英]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 没有默认的ColumnNameCaption 当您将其添加到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中。 我看到表达式语法支持可以在StringDateTime之间转换的CONVERT() function ,但我无法想象它会比 LINQ 更具性能或可读性,所以除非你绝对必须这样做,否则我不会追求它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM