Как удалить строки из DataTable до определенной даты? - PullRequest
0 голосов
/ 08 мая 2020

У меня проблемы с фильтрацией DataTable, скажем, DtFromExcel. DataTable НЕ имеет строки заголовка, и он начинается с фактической строки данных и выглядит примерно так:

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 без имени столбца. Я думал о назначении строки заголовка без перезаписи первой фактической строки данных, но, похоже, это большая работа, только для фильтрации. Во-вторых, я не уверен, как использовать эти условия ((а) второй столбец содержит определенную дату, AND (б) любую строку с датами, предшествующими этой определенной дате).

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);
    }
}

Я не хотел пропустить oop через весь DataTable, потому что этот процесс часто происходит в моей программе.

1 Ответ

0 голосов
/ 08 мая 2020

Если вы используете пустой конструктор для создания DataColumn без имени, в документации указано ...

При создании объект DataColumn не имеет по умолчанию ColumnName или Caption. Когда вы добавляете его к DataColumnCollection, будет сгенерировано имя по умолчанию ("Column1", "Column2" и т. Д.), Если имя не было присвоено ColumnName.

... поэтому создание и загрузка 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);
}

... производит этот вывод ...

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";

В любом случае вы у вас будет известное имя, по которому вы можете ссылаться на этот столбец.

Что касается вашего комментария о нежелании «l oop через весь DataTable», не ясно, имеете ли вы в виду из-за дополнительный код или, возможно, влияние на производительность, но до последнего пункта, даже если вы явно не l oop через и тестируете каждый 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. Я вижу, что синтаксис выражения поддерживает CONVERT() функцию , которая может преобразовывать между String и DateTime, но я не могу представить, что это было бы более производительным или читаемым чем LINQ, поэтому я бы не стал этим заниматься, если в этом нет крайней необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...