简体   繁体   中英

How to merge rows in a DataTable when data in multiple columns match?

I am trying to figure out a good way to merge rows in a DataTable when column1, column2, column3, column4 match. Does anyone have pointers or ideas of how this can be accomplished in VB.NET and/or 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  |

Here is a non-LINQ alternative. What it does is iterate each row in the first table. It then checks a secondary table to see if there are any rows in it that match the criteria. If there are, it adds the values in the other columns. If there aren't, it adds the entire row to the new table.

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

You could expand upon this to include more/less columns in your matching criteria and your addition logic. You could probably also think of something to get rid of the column number hardcoding such as iterating them up to a specific index or something. It all depends on your requirements. This should give you a decent starting point though.

Try this code using 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);
            }

I'd suggest taking a look at using DataTableExtensions, which will let you use LINQ to join the 2 data tables to populate the final data table.

I'll need to wait till tonight before I can post any sample code, but I have a feeling that you'll be able to figure out the required LINQ syntax before then.

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

You can use LINQ-To-DataTable , especially Enumerable.GroupBy . You have to use an anonymous type to group and a loop to create the new table with unique values.

I use Enumerable.Sum to get the sum of the last two columns:

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

I do not know the purpose of why you want to merge but, this might work.

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


            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM