【问题标题】:How to merge rows in a DataTable when data in multiple columns match?当多列中的数据匹配时,如何合并 DataTable 中的行?
【发布时间】:2014-10-15 13:55:06
【问题描述】:

我正在尝试找出一种在 column1、column2、column3、column4 匹配时合并 DataTable 中的行的好方法。有没有人有关于如何在 VB.NET 和/或 C# 中实现这一点的指示或想法?

DataTable with duplicate rows to merge 
-------------------------------------------------------------
| Column1 | Column2 | Column3 | Column4 | Column5 | Column6 |
-------------------------------------------------------------
| 123456  | 6       | 54      | 5       | 0.00    | 36.78   |
| 123456  | 6       | 54      | 5       | 21.00   | 0.00    |
| 123456  | 6       | 54      | 8       | 0.00    | 102.09  |
| 123456  | 6       | 54      | 8       | 6.50    | 0.00    |


Final DataTable with merged rows 
-------------------------------------------------------------
| Column1 | Column2 | Column3 | Column4 | Column5 | Column6 |
-------------------------------------------------------------
| 123456  | 6       | 54      | 5       | 21.00   | 36.78   |
| 123456  | 6       | 54      | 8       | 6.50    | 102.09  |

【问题讨论】:

  • 当你合并 Column5 和 Column6 时,你会如何处理它们?
  • 将这些值相加。 0.00 的单元格将是 0.00 或负值。
  • 听起来不错。到目前为止,您尝试过什么?
  • 不幸的是,我不能使用 LINQ,因为代码需要用 .NET 2.0 编写,但我应该能够根据发布的示例完成需要完成的工作。我会回来接受/投票最有用的答案。非常感谢您的建议。

标签: c# vb.net datatable


【解决方案1】:

这是一个非 LINQ 替代方案。它所做的是迭代第一个表中的每一行。然后它检查一个辅助表以查看其中是否有任何符合条件的行。如果有,它会添加其他列中的值。如果没有,它会将整行添加到新表中。

// Clone() only clones the table structure. It does not also clone the data.
DataTable dtFinal = dtOriginal.Clone();
for (int i = 0; i < dtOriginal.Rows.Count; i++)
{
    bool isDupe = false;
    for (int j = 0; j < dtFinal.Rows.Count; j++)
    {
        if (dtOriginal.Rows[i][0].ToString() == dtFinal.Rows[j][0].ToString()
            && dtOriginal.Rows[i][1].ToString() == dtFinal.Rows[j][1].ToString()
            && dtOriginal.Rows[i][2].ToString() == dtFinal.Rows[j][2].ToString())
        {
            dtFinal.Rows[j][3] = int.Parse(dtFinal.Rows[j][3].ToString()) + int.Parse(dtOriginal.Rows[i][3].ToString()); 
            isDupe = true;
            break;
        }
    }

    if (!isDupe)
    {
        dtFinal.ImportRow(dtOriginal.Rows[i]);
    }
}

您可以对此进行扩展,以在匹配条件和添加逻辑中包含更多/更少列。您可能还可以想到一些东西来摆脱列号硬编码,例如将它们迭代到特定索引或其他东西。这一切都取决于您的要求。不过,这应该会给您一个不错的起点。

【讨论】:

  • 这正是我想要的。非常感谢。
  • @j.f. dtFinal.Rows[j].Delete() 应该放在哪里,以便删除用作合并源的行,并且只保留新值在 dtFinal 中?
  • @Chris - 不完全确定你的意思。它本质上应该这样做。我只将适用的行添加到dtFinal。它不会将它们全部添加并删除。
【解决方案2】:

使用 linq 试试这个代码:

            DataTable dataTable1 = new DataTable();
            dataTable1.Columns.Add(new DataColumn("Column1", typeof(int)));
            dataTable1.Columns.Add(new DataColumn("Column2", typeof(int)));
            dataTable1.Columns.Add(new DataColumn("Column3", typeof(int)));
            dataTable1.Columns.Add(new DataColumn("Column4", typeof(int)));
            dataTable1.Columns.Add(new DataColumn("Column5", typeof(decimal)));
            dataTable1.Columns.Add(new DataColumn("Column6", typeof(decimal)));

            dataTable1.Rows.Add(123456, 6, 54, 5, 0, 36.78);
            dataTable1.Rows.Add(123456, 6, 54, 5, 21, 0);
            dataTable1.Rows.Add(123456, 6, 54, 8, 0, 102.09);
            dataTable1.Rows.Add(123456, 6, 54, 8, 6.50, 0);

            //Select the rows where columns 1-4 have repeated same values
            var distinctRows = dataTable1.AsEnumerable()
                                    .Select(s => new
                                    {
                                        unique1 = s.Field<int>("Column1"),
                                        unique2 = s.Field<int>("Column2"),
                                        unique3 = s.Field<int>("Column3"),
                                        unique4 = s.Field<int>("Column4"),
                                    })
                                    .Distinct();

            //Create a new datatable for the result
            DataTable resultDataTable = dataTable1.Clone();

            //Temporary variables
            DataRow newDataRow;
            IEnumerable<DataRow> results;
            decimal tempCol5;
            decimal tempCol6;

            //Go through each distinct rows to gather column5 and column6 values
            foreach (var item in distinctRows)
            {
                //create a new row for the result datatable
                newDataRow = resultDataTable.NewRow();

                //select all rows in original datatable with this distinct values
                results = dataTable1.Select().Where(
                    p => p.Field<int>("Column1") == item.unique1 
                    && p.Field<int>("Column2") == item.unique2 
                    && p.Field<int>("Column3") == item.unique3 
                    && p.Field<int>("Column4") == item.unique4);

                //Preserve column1 - 4 values
                newDataRow["Column1"] = item.unique1;
                newDataRow["Column2"] = item.unique2;
                newDataRow["Column3"] = item.unique3;
                newDataRow["Column4"] = item.unique4;

                //store here the sumns of column 5 and 6
                tempCol5 = 0;
                tempCol6 = 0;
                foreach (DataRow dr in results)
                {
                    tempCol5 += (decimal)dr["Column5"];
                    tempCol6 += (decimal)dr["Column6"];
                }

                //save those sumns in the new row
                newDataRow["Column5"] = tempCol5;
                newDataRow["Column6"] = tempCol6;

                //add the row to the result dataTable
                resultDataTable.Rows.Add(newDataRow);
            }

【讨论】:

    【解决方案3】:

    我建议看看使用 DataTableExtensions,它可以让您使用 LINQ 连接 2 个数据表以填充最终数据表。

    我需要等到今晚才能发布任何示例代码,但我感觉你可以在那之前弄清楚所需的 LINQ 语法。

    http://msdn.microsoft.com/en-us/library/system.data.datatableextensions(v=vs.110).aspx

    【讨论】:

      【解决方案4】:

      您可以使用LINQ-To-DataTable,尤其是Enumerable.GroupBy。您必须使用匿名类型进行分组,并使用循环来创建具有唯一值的新表。

      我使用Enumerable.Sum 得到最后两列的总和:

      Dim first4ColGroups = From row In table
                    Let first4colums = New With {
                      Key .col1 = row.Field(Of Int32)(0),
                      Key .col2 = row.Field(Of Int32)(1),
                      Key .col3 = row.Field(Of Int32)(2),
                      Key .col4 = row.Field(Of Int32)(3)
                    }
                    Group row By first4colums Into RowGroup = Group
      
      Dim tblUnique = table.Clone()  ' creates an empty table with the same columns '
      For Each grp In first4ColGroups
          Dim row As DataRow = tblUnique.Rows.Add()
          row.SetField(0, grp.first4colums.col1)
          row.SetField(1, grp.first4colums.col2)
          row.SetField(2, grp.first4colums.col3)
          row.SetField(3, grp.first4colums.col4)
          row.SetField(4, grp.RowGroup.Sum(Function(r) r.Field(Of Decimal)(4)))
          row.SetField(5, grp.RowGroup.Sum(Function(r) r.Field(Of Decimal)(5)))
      Next
      

      【讨论】:

        【解决方案5】:

        我不知道你为什么要合并的目的,但这可能会奏效。

        List<object> list = new List<object>();
                System.Data.DataTable DT2 = new System.Data.DataTable();
                private void button1_Click(object sender, EventArgs e)
                {
                    foreach (DataRow row in DT2.Rows)
                    {
                        for (int i = 0; i < DT2.Rows.Count; i++)
                        {
                            var Tjek = DT2.Rows[i][0];
                            if (list.Contains(DT2.Rows[i][0]))
                            {
        
                            }
                        else
                        {
        
        
                            list.Add(Tjek);
                        }
        
        
                    }
                }
            }
        

        【讨论】:

        • 上面的代码会在列表中加入一个用户名或其他唯一的东西,以确保没有欺骗
        猜你喜欢
        • 2015-04-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-10
        • 2018-12-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多