基于 VB.Net 的 DataTable 中的行项目删除重复行的最佳方法是什么?

What's The Best Way To Remove Duplicate Rows Based On A Row Item In A DataTable With VB.Net?

在互联网上阅读了多个 post 后,我无法弄清楚如何做到这一点,我只想在下面就这个问题做一个非常明确的 post。

如果有人指出有相同问题的文章对我有帮助,我将删除 post。

下面有一个示例数据 table。我想删除重复的行,但仅在 "Request Type" 为 "Cancel Order" 且 "Order Numbers" 相同的情况下。

Report Date Time, Order Number, Request Type, Old Value, New Value
12/5/2019 12:00 , TM123456-01 , Cancel Order, 470000000, 5700000000
12/5/2019 12:00 , TM123456-01 , Cancel Order , 123000000, 4560000000
12/5/2019 12:00 , MT123456-02 , Add Order    , 470000000, 5700000000
12/5/2019 12:00 , AP123456-02 , Add Order    , 470000000, 5700000000
12/5/2019 12:00 , ST123456-02 , Remove Order , 470000000, 5700000000

所以应该从上面的数据 table 中删除的行应该是第 2 行,因为它与第 1 行 "TM123456-01" 具有相同的 "Order Number" 并且它的请求类型是"Cancel Order".

我希望得到的结果是下面的数据 table。

Report Date Time, Order Number, Request Type, Old Value, New Value
12/5/2019 12:00 , TM123456-01 , Cancel Order, 470000000, 5700000000
12/5/2019 12:00 , MT123456-02 , Add Order    , 470000000, 5700000000
12/5/2019 12:00 , AP123456-02 , Add Order    , 470000000, 5700000000
12/5/2019 12:00 , ST123456-02 , Remove Order , 470000000, 5700000000

我知道我可能可以使用嵌套的 For Each 循环来执行此操作,但我想了解如何使用 Microsoft LINQ(如果可能)或其他方法以更优雅的方式执行此操作。

C#版本:

var result = orders.GroupBy(x => new { x.OrderNumber, x.RequestType})
            .SelectMany(x => x.Key.RequestType=="Cancel Order" ? x.Take(1) : x.ToList());

假设您要删除原始 table 中的行,而不是创建新的 table,您可以使用 LINQ 找到要删除的行,然后删除它们。 LINQ 用于查询数据,而不是修改数据。

Dim indicesOfRowsToDelete = dt.AsEnumerable _
                              .Select(Function(r, n) New With { Key r, Key n }) _
                              .GroupBy(Function(rn) New With { Key .OrderNumber = rn.r.Field(Of String)("OrderNumber"), Key .RequestType = rn.r.Field(Of String)("RequestType") }) _
                              .Where(Function(rg) rg.Key.RequestType = "Cancel Order") _
                              .SelectMany(Function(rg) rg.Skip(1).Select(Function(rn) rn.n)) _
                              .OrderByDescending(Function(n) n)

For Each n In indicesOfRowsToDelete
    dt.Rows(n).Delete
Next

这是相同代码的 C# 版本:

var indicesOfRowsToDelete = dt.AsEnumerable()
                              .Select((r, n) => new { r, n })
                              .GroupBy(rn => new { OrderNumber = rn.r.Field<string>("OrderNumber"), RequestType = rn.r.Field<string>("RequestType") })
                              .Where(rg => rg.Key.RequestType == "Cancel Order")
                              .SelectMany(rg => rg.Skip(1).Select(rn => rn.n))
                              .OrderByDescending(n => n);

foreach (var n in indicesOfRowsToDelete)
    dt.Rows[n].Delete();

但是,由于您发布的解决方案创建了一个包含所需行的新 table,这里是一个 LINQ 查询,用于在 C# 中创建一个新的 DataTable

var newDT = dt.AsEnumerable()
              .GroupBy(r => new { OrderNumber = r.Field<string>("OrderNumber"), RequestType = r.Field<string>("RequestType") })
              .SelectMany(rg => rg.Key.RequestType == "Cancel Order"
                                    ? rg.Take(1) // or other selection process
                                    : rg
              )
              .CopyToDataTable();

我为解决这个问题所做的是一个函数,它传入一个数据 Table 并输出一个数据 Table,其中包含我想要删除的重复项。

我使用 For Each 循环和 if 语句删除了重复项。我仍然相信应该有一种方法可以用 Linq 做到这一点。如果您 post 一个答案,我们将不胜感激,但现在,我会 post 我在下面。

请注意数据Table是输入和输出参数,因此它们不会在我的工作流程中声明。

Dim ListOfOrderNumbers As New List(Of String)

ForEach row in DataTable1

    If row.Item("RequestType").ToString = "Cancel Order" Then
        If ListOfOrderNumbers.Contains(row.Item("OrderNumber").ToString) Then
            'Do nothing
        Else
            DataTable2.Rows.Add(row.Item("ReportDateTime"), row.Item("OrderNumber").ToString, row.Item("RequestType").ToString, row.Item("OldValue").ToString, row.Item("NewValue").ToString)
            'Add the row to DataTabe2 since we know the order number is not in it yet.
            ListOfOrderNumbers.Add(row.Item("OrderNumber").ToString)
            'Add the OrderNumber to ListOfOrderNumbers so a row with the same OrderNumber doesn't get added to DataTable2 again.
    Else
        DataTable2.Rows.Add(ReportDateTime, OrderNumber, RequestType, OldValue, NewValue)