ما هي أفضل طريقة لإزالة التكرارات من DataTable؟

StackOverflow https://stackoverflow.com/questions/340223

  •  19-08-2019
  •  | 
  •  

سؤال

لقد راجعت الموقع بأكمله و googled على الشبكة ولكن لم أتمكن من إيجاد حل بسيط لهذه المشكلة.

لدي DataTable الذي يحتوي على حوالي 20 عمودًا و 10 آلاف صفوف. أحتاج إلى إزالة الصفوف المكررة في هذه البيانات المستندة إلى 4 أعمدة مفاتيح. لا .NET لديها وظيفة التي تفعل هذا؟ الوظيفة الأقرب إلى ما أبحث عنه كانت datatable.defaultView.totable (صواب ، مجموعة من الأعمدة لعرضها) ، ولكن هذه الوظيفة تعمل على مميزة الكل الاعمدة.

سيكون من الرائع أن يساعدني شخص ما في هذا.

تحرير: أنا آسف لعدم وضوح هذا. يتم إنشاء هذا dataTable عن طريق قراءة ملف CSV وليس من DB. لذا فإن استخدام استعلام SQL ليس خيارًا.

هل كانت مفيدة؟

المحلول

يمكنك استخدام LINQ إلى مجموعات البيانات. يفحص هذه. شيء من هذا القبيل:

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

نصائح أخرى

كيف يمكنني إزالة الصفوف المكررة؟. (اضبط الاستعلام هناك للانضمام إلى أعمدة المفاتيح الأربعة)

تحرير: مع معلوماتك الجديدة ، أعتقد أن أسهل طريقة هي تنفيذ iequalitycompareru003CT> واستخدام متميز على صفوف البيانات الخاصة بك. وإلا إذا كنت تعمل مع ienumerable/ilist بدلاً من DataTable/DataRow ، فمن المؤكد أنه من الممكن مع بعض الكائنات 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);

إذا كان لديك إمكانية الوصول إلى LINQ ، فأعتقد أنه يجب أن تكون قادرًا على استخدام وظائف المجموعة المضمنة على مجموعة الذاكرة واختيار الصفوف المكررة

ابحث في Google عن مجموعة LINQ عن طريق أمثلة

يجب أن تؤخذ في الاعتبار أن الجدول. يجب استدعاء AcceptChanges () لإكمال الحذف. خلاف ذلك ، لا يزال الصف المحذوف موجودًا في DataTable مع ضبط Rowstate. و Table.rows.count لم يتغير بعد الحذف.

أعتقد أن هذا يجب أن يكون أفضل طريقة لإزالة التكرارات من جدول البيانات باستخدام Linq و moreLinqشفرة:

LINQ

RemoveDuplicatesRecords(yourDataTable);


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

مقالة المدونة: قم بإزالة سجلات الصفوف المكررة من ASP.NET C# dataTable


موريلينك

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

ملحوظة: moreLinq تحتاج إلى إضافة مكتبة.

في موريلينك يمكنك استخدام وظيفة تسمى DimentBy والتي يمكنك تحديد خاصية تريد العثور عليها كائنات متميزة.

مقالة المدونة: باستخدام طريقة Morelinq المتميزة لإزالة السجلات المكررة

إجابة Liggett78 أفضل بكثير - esp. كما كان لي خطأ! تصحيح على النحو التالي ...

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

وجدت هذا على bytes.com:

يمكنك استخدام مزود Jet 4.0 OLE DB مع الفئات في مساحة الاسم System.Data.oledB للوصول إلى الملف النصي المحدد للفاصلة (باستخدام مجموعة بيانات/بيانات قابلة للاتصالات).

أو يمكنك استخدام برنامج تشغيل Microsoft النص لـ ODBC مع الفئات في مساحة اسم System.Data.odbc للوصول إلى الملف باستخدام برامج تشغيل ODBC.

سيتيح لك ذلك الوصول إلى بياناتك عبر استعلامات SQL ، كما اقترح آخرون.

"يتم إنشاء هذا dataTable عن طريق قراءة ملف CSV وليس من DB."

لذا ، ضع قيودًا فريدة على الأعمدة الأربعة في قاعدة البيانات ، وإدراج التكرارات تحت تصميمك لن تدخل. ما لم تقرر الفشل بدلاً من الاستمرار عند حدوث ذلك ، ولكن هذا بالتأكيد يمكن تكوينه في برنامج استيراد CSV الخاص بك.

استخدم استعلام بدلاً من الوظائف:

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

هذا رمز بسيط للغاية لا يتطلب 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.

جرب هذا

دعونا نفكر في DTInput هو جدول البيانات الخاص بك مع سجلات مكررة.

لديّ dtfinal قابلة للتصميم جديدة أرغب في تصفية الصفوف المكررة.

لذا فإن الكود الخاص بي سيكون مثل أدناه.

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

لم أكن حريصًا على استخدام حل 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;
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top