使用VB.Net删除基于数据表中行项的重复行的最佳方法是什么?

时间:2019-12-05 18:03:59

标签: vb.net linq filter datatable unique-values

在阅读了互联网上的多个帖子后,我不知道该怎么做,我只想在下面就这个问题做一个非常清晰的帖子。

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

我在下面有一个示例数据表。我想删除重复的行,但仅在“请求类型”为“取消订单”和“订单号”相同的情况下。

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

因此应从上述数据表中删除的行应为第2行,因为它与第1行具有相同的“订单号”,即“ TM123456-01”,其请求类型为“取消订单”。

我想留下的结果将是下面的数据表。

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(如果可能)或其他方法以更优雅的方式进行操作。

3 个答案:

答案 0 :(得分:1)

C#版本:

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

答案 1 :(得分:1)

假设您要从原始表中删除行,而不是创建新表,则可以使用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();

但是,由于您发布的解决方案会创建具有所需行的新表,因此这是一个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();

答案 2 :(得分:0)

我为解决此问题所做的工作是传递一个数据表并输出包含要删除的重复项的数据表的函数。

我使用For Each循环和if语句删除了重复项。我仍然相信使用Linq可以做到这一点。如果您发布答案,将不胜感激,但现在,我将在下面发布我的答案。

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

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)