Каков наилучший способ удаления дубликатов из таблицы данных? - PullRequest
7 голосов
/ 04 декабря 2008

Я проверил весь сайт и погуглил в сети, но не смог найти простого решения этой проблемы.

У меня есть таблица данных, которая имеет около 20 столбцов и 10K строк. Мне нужно удалить повторяющиеся строки в этой таблице данных на основе 4 ключевых столбцов. Разве .Net не имеет функцию, которая делает это? Функция, ближайшая к тому, что я ищу, была datatable.DefaultView.ToTable (true, массив отображаемых столбцов), но эта функция различает всех столбцов.

Было бы здорово, если бы кто-то мог помочь мне с этим.

РЕДАКТИРОВАТЬ: Я прошу прощения за то, что не ясно об этом. Эта таблица данных создается путем чтения файла CSV, а не из БД. Так что использование SQL-запроса не вариант.

Ответы [ 12 ]

8 голосов
/ 04 декабря 2008

Вы можете использовать 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"));
}
7 голосов
/ 04 декабря 2008

Как удалить повторяющиеся строки? . (Настройте запрос, чтобы присоединиться к вашим 4 ключевым столбцам)

РЕДАКТИРОВАТЬ: с вашей новой информацией я считаю, что самым простым способом было бы реализовать IEqualityComparer и использовать Distinct для ваших строк данных. В противном случае, если вы работаете с IEnumerable / IList вместо DataTable / DataRow, это возможно с некоторыми LINQ-to-objects kung-fu.

РЕДАКТИРОВАТЬ: пример 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 голос
/ 09 мая 2013

Я думаю, что это лучший способ удалить дубликаты из Datatable с помощью Linq и moreLinq Код:

Linq

RemoveDuplicatesRecords(yourDataTable);


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

Статья блога: Удаление записей дублирующихся строк из DataTable Asp.net c #


MoreLinq

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

Примечание: moreLinq необходимо добавить библиотеку.

В morelinq вы можете использовать функцию DistinctBy, в которой вы можете указать свойство, по которому вы хотите найти Distinct объекты.

Статья блога: Использование метода moreLinq DistinctBy для удаления дублирующихся записей

1 голос
/ 01 октября 2011

Следует учитывать, что Table.AcceptChanges () должен быть вызван для завершения удаления. В противном случае удаленная строка все еще присутствует в DataTable с RowState, установленным на Deleted. И Table.Rows.Count не изменяется после удаления.

1 голос
/ 04 декабря 2008

Если у вас есть доступ к Linq, я думаю, вы сможете использовать встроенную групповую функциональность в коллекции в памяти и выбрать дублирующиеся строки

Поиск в Google для Linq Group по примерам

0 голосов
/ 04 апреля 2013

Я не был заинтересован в использовании решения 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 голосов
/ 16 декабря 2011

Попробуйте это

Давайте рассмотрим dtInput - вашу таблицу данных с дублирующимися записями.

У меня есть новый DataTable dtFinal, в котором я хочу отфильтровать дублирующиеся строки.

Так что мой код будет примерно таким, как показано ниже.

DataTable dtFinal = dtInput.DefaultView.ToTable(true, 
                           new string[ColumnCount] {"Col1Name","Col2Name","Col3Name",...,"ColnName"});
0 голосов
/ 27 мая 2010

Это очень простой код, который не требует 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.

0 голосов
/ 04 декабря 2008

"Этот объект данных создается путем чтения файла CSV, а не из БД."

Итак, наложите уникальное ограничение на четыре столбца в базе данных, и вставки, которые являются дубликатами в вашем дизайне, не будут задействованы. Если только он не решит потерпеть неудачу, вместо продолжения, когда это произойдет, но это, безусловно, настраивается в вашем CSV скрипт импорта.

0 голосов
/ 04 декабря 2008

Нашел это на bytes.com :

Вы можете использовать провайдер JET 4.0 OLE DB с классами в Пространство имен System.Data.OleDb для доступа к текстовому файлу с разделителями-запятыми (используя DataSet / DataTable).

Или вы можете использовать Microsoft Text Driver для ODBC с классами в Пространство имен System.Data.Odbc для доступа к файлу с помощью драйверов ODBC.

Это позволит вам получить доступ к вашим данным с помощью SQL-запросов, как предлагали другие.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...