0

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

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

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

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 或其他方法以更优雅的方式执行此操作。

4

3 回答 3

1

C#版本:

var result = orders.GroupBy(x => new { x.OrderNumber, x.RequestType})
            .SelectMany(x => x.Key.RequestType=="Cancel Order" ? x.Take(1) : x.ToList());
于 2019-12-05T19:14:44.813 回答
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 查询,用于DataTable在 C# 中创建一个新表:

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();
于 2019-12-05T21:24:30.440 回答
0

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

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

请注意,DataTables 是 in 和 out 参数,因此它们不会在我的工作流程中声明。

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)
于 2019-12-05T21:34:19.780 回答