从数据表中删除重复项

问题描述 投票:0回答:10

我有一个大约有 20 列和 10K 行的数据表。我需要根据 4 个关键列删除此数据表中的重复行。 .Net 没有一个函数可以做到这一点吗?最接近我正在寻找的函数是 datatable.DefaultView.ToTable(true,要显示的列数组),但是这个函数对all列有不同的作用。

编辑:此数据表是通过读取 CSV 文件而不是从数据库创建的,因此不能选择使用 SQL 查询。

c# datatable duplicates filtering
10个回答
9
投票

您可以使用 Linq to Datasets。检查这个。像这样的东西:

// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

List<DataRow> rows = new List<DataRow>();

DataTable contact = ds.Tables["Contact"];

// Get 100 rows from the Contact table.
IEnumerable<DataRow> query = (from c in contact.AsEnumerable()
                              select c).Take(100);

DataTable contactsTableWith100Rows = query.CopyToDataTable();

// Add 100 rows to the list.
foreach (DataRow row in contactsTableWith100Rows.Rows)
    rows.Add(row);

// Create duplicate rows by adding the same 100 rows to the list.
foreach (DataRow row in contactsTableWith100Rows.Rows)
    rows.Add(row);

DataTable table =
    System.Data.DataTableExtensions.CopyToDataTable<DataRow>(rows);

// Find the unique contacts in the table.
IEnumerable<DataRow> uniqueContacts =
    table.AsEnumerable().Distinct(DataRowComparer.Default);

Console.WriteLine("Unique contacts:");
foreach (DataRow uniqueContact in uniqueContacts)
{
    Console.WriteLine(uniqueContact.Field<Int32>("ContactID"));
}

8
投票

我相信最简单的方法是实现

IEqualityComparer<T>
并在数据行上使用 Distinct。否则,如果您使用 IEnumerable/IList 而不是 DataTable/DataRow,那么使用一些 LINQ 到对象功夫当然是可能的。

IEqualityComparer 示例

public class MyRowComparer : IEqualityComparer<DataRow>
{
 
    public bool Equals(DataRow x, DataRow y)
    {
        return (x.Field<int>("ID") == y.Field<int>("ID")) &&
            string.Compare(x.Field<string>("Name"), y.Field<string>("Name"), true) == 0 &&
          ... // extend this to include all your 4 keys...
    }
 
    public int GetHashCode(DataRow obj)
    {
        return obj.Field<int>("ID").GetHashCode() ^ obj.Field<string>("Name").GetHashCode() etc.
    }
}

你可以这样使用它:

var uniqueRows = myTable.AsEnumerable().Distinct(MyRowComparer);

1
投票

我认为这一定是使用 Linq

moreLinq
Datatable
中删除重复项的最佳方法 代码:

Linq

RemoveDuplicatesRecords(yourDataTable);


private DataTable RemoveDuplicatesRecords(DataTable dt)
{
    var UniqueRows = dt.AsEnumerable().Distinct(DataRowComparer.Default);
    DataTable dt2 = UniqueRows.CopyToDataTable();
    return dt2;
}

更多Linq

// Distinctby  column name ID 
var valueDistinctByIdColumn = yourTable.AsEnumerable().DistinctBy(row => new { Id = row["Id"] });
DataTable dtDistinctByIdColumn = valueDistinctByIdColumn.CopyToDataTable();
 

注意:

moreLinq
需要添加库。

morelinq 中,您可以使用名为 DistinctBy 的函数,在其中您可以指定要在其上查找 Distinct 对象的属性。


1
投票

需要注意的是,必须调用

Table.AcceptChanges()
才能完成删除。否则,已删除的行仍存在于
DataTable
中,且
RowState
设置为已删除。并且
Table.Rows.Count
删除后不会改变。


0
投票

如果您有权访问 Linq,我认为您应该能够使用内存集合中的内置组功能并挑选出重复的行

在 Google 中搜索 Linq Group by 示例


0
投票

bytes.com 上找到了这个:

您可以将 JET 4.0 OLE DB 提供程序与以下类中的类一起使用 System.Data.OleDb 命名空间来访问逗号分隔的文本文件(使用 数据集/数据表)。

或者您可以将 Microsoft Text Driver for ODBC 与 System.Data.Odbc 命名空间使用 ODBC 驱动程序访问文件。

这将允许您通过 sql 查询访问数据,正如其他人建议的那样。


0
投票

我不热衷于使用上面的 Linq 解决方案,所以我写了这个:

/// <summary>
/// Takes a datatable and a column index, and returns a datatable without duplicates
/// </summary>
/// <param name="dt">The datatable containing duplicate records</param>
/// <param name="ComparisonFieldIndex">The column index containing duplicates</param>
/// <returns>A datatable object without duplicated records</returns>
public DataTable duplicateRemoval(DataTable dt, int ComparisonFieldIndex)
{
    try
    {
        //Build the new datatable that will be returned
        DataTable dtReturn = new DataTable();
        for (int i = 0; i < dt.Columns.Count; i++)
        {
            dtReturn.Columns.Add(dt.Columns[i].ColumnName, System.Type.GetType("System.String"));
        }

        //Loop through each record in the datatable we have been passed
        foreach (DataRow dr in dt.Rows)
        {
            bool Found = false;
            //Loop through each record already present in the datatable being returned
            foreach (DataRow dr2 in dtReturn.Rows)
            {
                bool Identical = true;
                //Compare the column specified to see if it matches an existing record
                if (!(dr2[ComparisonFieldIndex].ToString() == dr[ComparisonFieldIndex].ToString()))
                {
                    Identical = false;
                }
                //If the record found identically matches one we already have, don't add it again
                if (Identical)
                {
                    Found = true;
                    break;
                }
            }
            //If we didn't find a matching record, we'll add this one
            if (!Found)
            {
                DataRow drAdd = dtReturn.NewRow();
                for (int i = 0; i < dtReturn.Columns.Count; i++)
                {
                    drAdd[i] = dr[i];
                }

                dtReturn.Rows.Add(drAdd);
            }
        }
        return dtReturn;
    }
    catch (Exception)
    {
        //Return the original datatable if something failed above
        return dt;
    }
}

此外,这适用于所有列,而不是特定的列索引:

/// <summary>
/// Takes a datatable and returns a datatable without duplicates
/// </summary>
/// <param name="dt">The datatable containing duplicate records</param>
/// <returns>A datatable object without duplicated records</returns>
public DataTable duplicateRemoval(DataTable dt)
{
    try
    {
        //Build the new datatable that will be returned
        DataTable dtReturn = new DataTable();
        for (int i = 0; i < dt.Columns.Count; i++)
        {
            dtReturn.Columns.Add(dt.Columns[i].ColumnName, System.Type.GetType("System.String"));
        }

        //Loop through each record in the datatable we have been passed
        foreach (DataRow dr in dt.Rows)
        {
            bool Found = false;
            //Loop through each record already present in the datatable being returned
            foreach (DataRow dr2 in dtReturn.Rows)
            {
                bool Identical = true;
                //Compare all columns to see if they match the existing record
                for (int i = 0; i < dt.Columns.Count; i++)
                {
                    if (!(dr2[i].ToString() == dr[i].ToString()))
                    {
                        Identical = false;
                    }
                }
                //If the record found identically matches one we already have, don't add it again
                if (Identical)
                {
                    Found = true;
                    break;
                }
            }
            //If we didn't find a matching record, we'll add this one
            if (!Found)
            {
                DataRow drAdd = dtReturn.NewRow();
                for (int i = 0; i < dtReturn.Columns.Count; i++)
                {
                    drAdd[i] = dr[i];
                }

                dtReturn.Rows.Add(drAdd);
            }
        }
        return dtReturn;
    }
    catch (Exception)
    {
        //Return the original datatable if something failed above
        return dt;
    }
}

0
投票

为了完整起见,我附上了一个基于此处已有的一些答案的示例。当其余列可能不同时,此解决方案按 fieldKey1 到 N 过滤表。但也会过滤重复项中匹配的第一个,即其他两列的最低值:

return dt.AsEnumerable()
    .Distinct(DataRowComparer.Default)
    .GroupBy(r => new
    {
        fieldKey1 = r.Field<int>("fieldKey1"), 
        fieldKey2 = r.Field<string>("fieldKey2"), 
        fieldKeyn = r.Field<DateTime>("fieldKeyn")
    })
    .Select(g =>  
        g.OrderBy( dr => dr.Field<int>( "OtherField1" ) )
            .ThenBy( dr => dr.Field<int>( "OtherField2" ) )
                .First())
    .CopyToDataTable();

所以数据表dt:

字段键1 字段键2 fieldKeyn 其他领域1 其他领域2 其他领域3
1 两个 2020年12月31日 4 3 xyz7
2 其他 2021年12月31日 4 3 xyz100
1 两个 2020年12月31日 2 2 xyz3
1 两个 2020年12月31日 2 3 xyz4
1 两个 2020年12月31日 1 2 xyz1
1 两个 2020年12月31日 1 4 xyz2
1 两个 2020年12月31日 3 3 xyz5
1 两个 2020年12月31日 3 3 xyz6

会回来:

字段键1 字段键2 fieldKeyn 其他领域1 其他领域2 其他领域3
1 两个 2020年12月31日 1 2 xyz1
2 其他 2021年12月31日 4 3 xyz100

0
投票

此代码不需要 LINQ 或单个列来执行过滤。如果一行中所有列的值都为空,则该行将被删除。

public DataSet duplicateRemoval(DataSet dSet) 
{
    bool flag;
    int ccount = dSet.Tables[0].Columns.Count;
    string[] colst = new string[ccount];
    int p = 0;

    DataSet dsTemp = new DataSet();
    DataTable Tables = new DataTable();
    dsTemp.Tables.Add(Tables);

    for (int i = 0; i < ccount; i++)
    {
        dsTemp.Tables[0].Columns.Add(dSet.Tables[0].Columns[i].ColumnName, System.Type.GetType("System.String"));
    }
 
    foreach (System.Data.DataRow row in dSet.Tables[0].Rows)
    {
        flag = false;
        p = 0;
        foreach (System.Data.DataColumn col in dSet.Tables[0].Columns)
        {
            colst[p++] = row[col].ToString();
            if (!string.IsNullOrEmpty(row[col].ToString()))
            {  //Display only if any of the data is present in column
                flag = true;
            }
        }
        if (flag == true)
        {
            DataRow myRow = dsTemp.Tables[0].NewRow();
            //Response.Write("<tr style=\"background:#d2d2d2;\">");
            for (int kk = 0; kk < ccount; kk++)
            {
                myRow[kk] = colst[kk];         
               
                // Response.Write("<td class=\"table-line\" bgcolor=\"#D2D2D2\">" + colst[kk] + "</td>");
            }
            dsTemp.Tables[0].Rows.Add(myRow);
        }
    } return dsTemp;
}

这甚至可以用于从 Excel 工作表中删除空数据。


-1
投票

让我们考虑一下

dtInput
是您的数据表,其中有重复的记录。

我有一个新的数据表

dtFinal
,我想在其中过滤重复的行。

所以我的代码将如下所示:

DataTable dtFinal = dtInput.DefaultView.ToTable(true, new string[ColumnCount]{"Col1Name","Col2Name","Col3Name",...,"ColnName"});

其中

ColumnCount
是您拥有的列数。

© www.soinside.com 2019 - 2024. All rights reserved.