Pregunta

Revisé todo el sitio y busqué en Google en la red, pero no pude encontrar una solución simple a este problema.

Tengo una tabla de datos que tiene alrededor de 20 columnas y 10K filas.Necesito eliminar las filas duplicadas en esta tabla de datos en función de 4 columnas clave.¿No tiene .Net una función que haga esto?La función más cercana a lo que estoy buscando era datatable.DefaultView.ToTable (verdadero, conjunto de columnas para mostrar), pero esta función hace una diferencia todo las columnas.

Sería genial si alguien pudiera ayudarme con esto.

EDITAR:Lamento no haber sido claro en esto.Esta tabla de datos se crea leyendo un archivo CSV y no desde una base de datos.Entonces usar una consulta SQL no es una opción.

¿Fue útil?

Solución

Puede usar Linq para conjuntos de datos. Verifique esto . Algo como esto:

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

Otros consejos

¿Cómo puedo eliminar filas duplicadas? . (Ajuste la consulta allí para unirse en sus 4 columnas clave)

EDITAR: con su nueva información, creo que la forma más fácil sería implementar IEqualityComparer < T > y use Distinct en sus filas de datos. De lo contrario, si está trabajando con IEnumerable / IList en lugar de DataTable / DataRow, ciertamente es posible con algunos kung-fu de LINQ-to-objects.

EDITAR: ejemplo 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.
    }
}

Puedes usarlo así:

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

Si tiene acceso a Linq, creo que debería poder usar la funcionalidad de grupo integrada en la colección en memoria y seleccionar las filas duplicadas

Busque en Google el grupo Linq por ejemplos

Debe tenerse en cuenta que se debe llamar a Table.AcceptChanges () para completar la eliminación. De lo contrario, la fila eliminada todavía está presente en DataTable con RowState establecido en Eliminado. Y Table.Rows.Count no cambia después de la eliminación.

Creo que esta debe ser la mejor manera de eliminar duplicados de Tabla de datos mediante el uso Linq y moreLinqCódigo:

Linq

RemoveDuplicatesRecords(yourDataTable);


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

Artículo de blog: Eliminar registros de filas duplicadas de DataTable Asp.net c#


MásLinq

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

Nota: moreLinq Es necesario agregar una biblioteca.

En morelinq puede utilizar la función llamada DistinctBy en la que puede especificar la propiedad en la que desea encontrar objetos Distinct.

Artículo de blog: Uso del método moreLinq DistinctBy para eliminar registros duplicados

La respuesta de Liggett78 es mucho mejor, especialmente. como el mío tuvo un error! Corrección de la siguiente manera ...

DELETE TableWithDuplicates
    FROM TableWithDuplicates
        LEFT OUTER JOIN (
            SELECT PK_ID = Min(PK_ID), --Decide your method for deciding which rows to keep
                KeyColumn1,
                KeyColumn2,
                KeyColumn3,
                KeyColumn4
                FROM TableWithDuplicates
                GROUP BY KeyColumn1,
                    KeyColumn2,
                    KeyColumn3,
                    KeyColumn4
            ) AS RowsToKeep
            ON TableWithDuplicates.PK_ID = RowsToKeep.PK_ID
    WHERE RowsToKeep.PK_ID IS NULL

Encontré esto en bytes.com :

  

Puede usar el proveedor JET 4.0 OLE DB con las clases en el   Espacio de nombres System.Data.OleDb para acceder al archivo de texto delimitado por comas (utilizando un   DataSet / DataTable).

     

O podría usar Microsoft Text Driver para ODBC con las clases en el   Espacio de nombres System.Data.Odbc para acceder al archivo utilizando controladores ODBC.

Eso le permitiría acceder a sus datos a través de consultas sql, como lo propusieron otros.

" Esta tabla de datos se está creando leyendo un archivo CSV y no desde un DB. "

Entonces, imponga una restricción única en las cuatro columnas de la base de datos, y las inserciones que están duplicadas en su diseño no entrarán. A menos que decida fallar en lugar de continuar cuando esto suceda, pero esto seguramente es configurable en su CSV Importar script.

Use una consulta en lugar de funciones:

DELETE FROM table1 AS tb1 INNER JOIN 
(SELECT id, COUNT(id) AS cntr FROM table1 GROUP BY id) AS tb2
ON tb1.id = tb2.id WHERE tb2.cntr > 1

Este es un código muy simple que no requiere linq ni columnas individuales para hacer el filtro. Si todos los valores de las columnas en una fila son nulos, se eliminarán.


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

Esto incluso puede usarse para eliminar datos nulos de la hoja de Excel.

Prueba esto

Consideremos que dtInput es su tabla de datos con registros duplicados.

Tengo un nuevo DataTable dtFinal en el que quiero filtrar las filas duplicadas.

Por lo tanto, mi código será similar al siguiente.

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

No estaba interesado en usar la solución de Linq anterior, así que escribí esto:

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

Además, esto funciona en TODAS las columnas en lugar de un índice de columna específico:

/// <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;
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top