سؤال

هل لدى أي شخص طريقة سريعة لإلغاء تكرار قائمة عامة في C#؟

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

المحلول

ربما يجب عليك التفكير في استخدام أ HashSet.

من رابط MSDN:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        HashSet<int> evenNumbers = new HashSet<int>();
        HashSet<int> oddNumbers = new HashSet<int>();

        for (int i = 0; i < 5; i++)
        {
            // Populate numbers with just even numbers.
            evenNumbers.Add(i * 2);

            // Populate oddNumbers with just odd numbers.
            oddNumbers.Add((i * 2) + 1);
        }

        Console.Write("evenNumbers contains {0} elements: ", evenNumbers.Count);
        DisplaySet(evenNumbers);

        Console.Write("oddNumbers contains {0} elements: ", oddNumbers.Count);
        DisplaySet(oddNumbers);

        // Create a new HashSet populated with even numbers.
        HashSet<int> numbers = new HashSet<int>(evenNumbers);
        Console.WriteLine("numbers UnionWith oddNumbers...");
        numbers.UnionWith(oddNumbers);

        Console.Write("numbers contains {0} elements: ", numbers.Count);
        DisplaySet(numbers);
    }

    private static void DisplaySet(HashSet<int> set)
    {
        Console.Write("{");
        foreach (int i in set)
        {
            Console.Write(" {0}", i);
        }
        Console.WriteLine(" }");
    }
}

/* This example produces output similar to the following:
 * evenNumbers contains 5 elements: { 0 2 4 6 8 }
 * oddNumbers contains 5 elements: { 1 3 5 7 9 }
 * numbers UnionWith oddNumbers...
 * numbers contains 10 elements: { 0 2 4 6 8 1 3 5 7 9 }
 */

نصائح أخرى

إذا كنت تستخدم ‎.Net 3+، فيمكنك استخدام Linq.

List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();

ماذا عن:-

var noDupes = list.Distinct().ToList();

في نت 3.5؟

ما عليك سوى تهيئة HashSet بقائمة من نفس النوع:

var noDupes = new HashSet<T>(withDupes);

أو، إذا كنت تريد إرجاع القائمة:

var noDupsList = new HashSet<T>(withDupes).ToList();

قم بفرزها، ثم حدد اثنين واثنين بجانب بعضهما البعض، حيث أن التكرارات سوف تتجمع معًا.

شيء من هذا القبيل:

list.Sort();
Int32 index = list.Count - 1;
while (index > 0)
{
    if (list[index] == list[index - 1])
    {
        if (index < list.Count - 1)
            (list[index], list[list.Count - 1]) = (list[list.Count - 1], list[index]);
        list.RemoveAt(list.Count - 1);
        index--;
    }
    else
        index--;
}

ملحوظات:

  • تتم المقارنة من الخلف إلى الأمام، لتجنب الاضطرار إلى اللجوء إلى القائمة بعد كل عملية إزالة
  • يستخدم هذا المثال الآن C# Value Tuples لإجراء عملية التبديل، واستبدلها بالكود المناسب إذا لم تتمكن من استخدام ذلك
  • لم يعد يتم فرز النتيجة النهائية

عملت معي.ببساطة استخدم

List<Type> liIDs = liIDs.Distinct().ToList<Type>();

استبدل "النوع" بالنوع الذي تريده، على سبيل المثال.كثافة العمليات.

أحب استخدام هذا الأمر:

List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
                                                 .GroupBy(s => s.City)
                                                 .Select(grp => grp.FirstOrDefault())
                                                 .OrderBy(s => s.City)
                                                 .ToList();

لدي هذه الحقول في قائمتي:ID ، storename ، City ، PostalCode أردت أن أعرض قائمة بالمدن في منسدلة لها قيم مكررة.حل:قم بالتجميع حسب المدينة ثم اختر المدينة الأولى من القائمة.

اتمني ان يكون مفيدا :)

كما قال كرونوز في .Net 3.5 يمكنك استخدامه Distinct().

في .Net 2 يمكنك تقليده:

public IEnumerable<T> DedupCollection<T> (IEnumerable<T> input) 
{
    var passedValues = new HashSet<T>();

    // Relatively simple dupe check alg used as example
    foreach(T item in input)
        if(passedValues.Add(item)) // True if item is new
            yield return item;
}

يمكن استخدام هذا لحذف أي مجموعة وإرجاع القيم بالترتيب الأصلي.

عادةً ما يكون تصفية المجموعة أسرع بكثير (حيث أن كليهما Distinct() وهذه العينة تفعل ذلك) مما قد يكون لإزالة العناصر منه.

قد تكون طريقة التمديد طريقة جيدة للذهاب ...شيء من هذا القبيل:

public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
    return listToDeduplicate.Distinct().ToList();
}

ثم اتصل هكذا مثلا:

List<int> myFilteredList = unfilteredList.Deduplicate();

في Java (أفترض أن C# متطابق إلى حد ما):

list = new ArrayList<T>(new HashSet<T>(list))

إذا كنت تريد حقًا تغيير القائمة الأصلية:

List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);

للحفاظ على النظام، ما عليك سوى استبدال HashSet بـ LinkedHashSet.

استخدم لينك اتحاد طريقة.

ملحوظة:هذا الحل لا يتطلب أي معرفة بـ Linq، بخلاف أنه موجود.

شفرة

ابدأ بإضافة ما يلي إلى أعلى ملف الفصل الدراسي الخاص بك:

using System.Linq;

الآن، يمكنك استخدام ما يلي لإزالة التكرارات من كائن يسمى، obj1:

obj1 = obj1.Union(obj1).ToList();

ملحوظة:إعادة تسمية obj1 إلى اسم الكائن الخاص بك.

كيف تعمل

  1. يسرد أمر الاتحاد واحدًا من كل إدخال لكائنين مصدريين.نظرًا لأن obj1 عبارة عن كائنين مصدرين، يؤدي هذا إلى تقليل obj1 إلى واحد من كل إدخال.

  2. ال ToList() إرجاع قائمة جديدة.وهذا أمر ضروري، لأن أوامر Linq مثل Union تقوم بإرجاع النتيجة كنتيجة IEnumerable بدلاً من تعديل القائمة الأصلية أو إرجاع قائمة جديدة.

إذا كنت لا تهتم بالترتيب، يمكنك فقط وضع العناصر في ملف HashSet, ، اذا أنت يفعل تريد الحفاظ على الترتيب، يمكنك القيام بشيء مثل هذا:

var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
    if (hs.Add(t))
        unique.Add(t);

أو طريقة لينك:

var hs = new HashSet<T>();
list.All( x =>  hs.Add(x) );

يحرر: ال HashSet الطريقة هي O(N) وقت و O(N) مساحة أثناء الفرز ثم جعلها فريدة (كما اقترح @lassevk وغيرهم) هو O(N*lgN) وقت و O(1) مساحة لذلك ليس من الواضح بالنسبة لي (كما كان للوهلة الأولى) أن طريقة الفرز أقل شأنا (اعتذاري عن التصويت السلبي المؤقت ...)

فيما يلي طريقة تمديد لإزالة التكرارات المجاورة في الموقع.قم باستدعاء Sort() أولاً ثم قم بتمرير نفس IComparer.يجب أن يكون هذا أكثر كفاءة من Lasse V.إصدار Karlsen الذي يستدعي RemoveAt بشكل متكرر (مما يؤدي إلى تحركات متعددة للذاكرة).

public static void RemoveAdjacentDuplicates<T>(this List<T> List, IComparer<T> Comparer)
{
    int NumUnique = 0;
    for (int i = 0; i < List.Count; i++)
        if ((i == 0) || (Comparer.Compare(List[NumUnique - 1], List[i]) != 0))
            List[NumUnique++] = List[i];
    List.RemoveRange(NumUnique, List.Count - NumUnique);
}

كطريقة مساعدة (بدون Linq):

public static List<T> Distinct<T>(this List<T> list)
{
    return (new HashSet<T>(list)).ToList();
}

تثبيت المزيدLINQ الحزمة عبر Nuget، يمكنك بسهولة تمييز قائمة الكائنات حسب الخاصية

IEnumerable<Catalogue> distinctCatalogues = catalogues.DistinctBy(c => c.CatalogueCode); 

يأخذ هذا العناصر المميزة (بدون عناصر مكررة) ويحولها إلى قائمة مرة أخرى:

List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();

قد يكون من الأسهل التأكد من عدم إضافة التكرارات إلى القائمة.

if(items.IndexOf(new_item) < 0) 
    items.add(new_item)

طريقة أخرى في .Net 2.0

    static void Main(string[] args)
    {
        List<string> alpha = new List<string>();

        for(char a = 'a'; a <= 'd'; a++)
        {
            alpha.Add(a.ToString());
            alpha.Add(a.ToString());
        }

        Console.WriteLine("Data :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t); });

        alpha.ForEach(delegate (string v)
                          {
                              if (alpha.FindAll(delegate(string t) { return t == v; }).Count > 1)
                                  alpha.Remove(v);
                          });

        Console.WriteLine("Unique Result :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t);});
        Console.ReadKey();
    }

هناك العديد من الطرق لحلها - مشكلة التكرارات في القائمة، وفيما يلي إحدى هذه الطرق:

List<Container> containerList = LoadContainer();//Assume it has duplicates
List<Container> filteredList = new  List<Container>();
foreach (var container in containerList)
{ 
  Container duplicateContainer = containerList.Find(delegate(Container checkContainer)
  { return (checkContainer.UniqueId == container.UniqueId); });
   //Assume 'UniqueId' is the property of the Container class on which u r making a search

    if(!containerList.Contains(duplicateContainer) //Add object when not found in the new class object
      {
        filteredList.Add(container);
       }
  }

هتاف رافي غانيسان

إليك حل بسيط لا يتطلب أي LINQ يصعب قراءته أو أي فرز مسبق للقائمة.

   private static void CheckForDuplicateItems(List<string> items)
    {
        if (items == null ||
            items.Count == 0)
            return;

        for (int outerIndex = 0; outerIndex < items.Count; outerIndex++)
        {
            for (int innerIndex = 0; innerIndex < items.Count; innerIndex++)
            {
                if (innerIndex == outerIndex) continue;
                if (items[outerIndex].Equals(items[innerIndex]))
                {
                    // Duplicate Found
                }
            }
        }
    }

تعد إجابة David J. طريقة جيدة، ولا حاجة إلى كائنات إضافية أو فرز أو ما إلى ذلك.ومع ذلك يمكن تحسينه:

for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)

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

تتأكد الحلقة الخارجية من معالجة القائمة بأكملها، وتبحث الحلقة الداخلية عن التكرارات الفعلية، ويمكن أن يحدث ذلك فقط في الجزء الذي لم تعالجه الحلقة الخارجية بعد.

أو إذا كنت لا ترغب في تنفيذ الحلقة الداخلية من الأسفل إلى الأعلى، فيمكنك جعل الحلقة الداخلية تبدأ عند ExternalIndex + 1.

يمكنك استخدام الاتحاد

obj2 = obj1.Union(obj1).ToList();

إذا كان لديك فصول السحب Product و Customer ونريد إزالة العناصر المكررة من قائمتهم

public class Product
{
    public int Id { get; set; }
    public string ProductName { get; set; }

}

public class Customer
{
    public int Id { get; set; }
    public string CustomerName { get; set; }

}

يجب عليك تحديد فئة عامة في النموذج أدناه

public class ItemEqualityComparer<T> : IEqualityComparer<T> where T : class
{
    private readonly PropertyInfo _propertyInfo;

    public ItemEqualityComparer(string keyItem)
    {
        _propertyInfo = typeof(T).GetProperty(keyItem, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
    }

    public bool Equals(T x, T y)
    {
        var xValue = _propertyInfo?.GetValue(x, null);
        var yValue = _propertyInfo?.GetValue(y, null);
        return xValue != null && yValue != null && xValue.Equals(yValue);
    }

    public int GetHashCode(T obj)
    {
        var propertyValue = _propertyInfo.GetValue(obj, null);
        return propertyValue == null ? 0 : propertyValue.GetHashCode();
    }
}

ثم يمكنك إزالة العناصر المكررة في قائمتك.

var products = new List<Product>
            {
                new Product{ProductName = "product 1" ,Id = 1,},
                new Product{ProductName = "product 2" ,Id = 2,},
                new Product{ProductName = "product 2" ,Id = 4,},
                new Product{ProductName = "product 2" ,Id = 4,},
            };
var productList = products.Distinct(new ItemEqualityComparer<Product>(nameof(Product.Id))).ToList();

var customers = new List<Customer>
            {
                new Customer{CustomerName = "Customer 1" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
            };
var customerList = customers.Distinct(new ItemEqualityComparer<Customer>(nameof(Customer.Id))).ToList();

يقوم هذا الرمز بإزالة العناصر المكررة بواسطة Id إذا كنت تريد إزالة العناصر المكررة بواسطة خاصية أخرى، يمكنك التغيير nameof(YourClass.DuplicateProperty) نفس nameof(Customer.CustomerName) ثم قم بإزالة العناصر المكررة بواسطة CustomerName ملكية.

  public static void RemoveDuplicates<T>(IList<T> list )
  {
     if (list == null)
     {
        return;
     }
     int i = 1;
     while(i<list.Count)
     {
        int j = 0;
        bool remove = false;
        while (j < i && !remove)
        {
           if (list[i].Equals(list[j]))
           {
              remove = true;
           }
           j++;
        }
        if (remove)
        {
           list.RemoveAt(i);
        }
        else
        {
           i++;
        }
     }  
  }

تنفيذ بديهي بسيط:

public static List<PointF> RemoveDuplicates(List<PointF> listPoints)
{
    List<PointF> result = new List<PointF>();

    for (int i = 0; i < listPoints.Count; i++)
    {
        if (!result.Contains(listPoints[i]))
            result.Add(listPoints[i]);
        }

        return result;
    }

تقوم جميع الإجابات بنسخ القوائم، أو إنشاء قائمة جديدة، أو استخدام وظائف بطيئة، أو أنها بطيئة للغاية.

على حد فهمي، هذا هو الطريقة الأسرع والأرخص أعرف ذلك (أيضًا، مدعومًا بمبرمج ذي خبرة كبيرة ومتخصص في تحسين الفيزياء في الوقت الفعلي).

// Duplicates will be noticed after a sort O(nLogn)
list.Sort();

// Store the current and last items. Current item declaration is not really needed, and probably optimized by the compiler, but in case it's not...
int lastItem = -1;
int currItem = -1;

int size = list.Count;

// Store the index pointing to the last item we want to keep in the list
int last = size - 1;

// Travel the items from last to first O(n)
for (int i = last; i >= 0; --i)
{
    currItem = list[i];

    // If this item was the same as the previous one, we don't want it
    if (currItem == lastItem)
    {
        // Overwrite last in current place. It is a swap but we don't need the last
       list[i] = list[last];

        // Reduce the last index, we don't want that one anymore
        last--;
    }

    // A new item, we store it and continue
    else
        lastItem = currItem;
}

// We now have an unsorted list with the duplicates at the end.

// Remove the last items just once
list.RemoveRange(last + 1, size - last - 1);

// Sort again O(n logn)
list.Sort();

التكلفة النهائية هي:

nlogn + n + nlogn = n + 2nlogn = يا (تسجيل الدخول) وهو لطيف جدًا.

ملاحظة حول RemoveRange:نظرًا لأننا لا نستطيع ضبط عدد القائمة وتجنب استخدام وظائف الإزالة، فأنا لا أعرف بالضبط سرعة هذه العملية ولكن أعتقد أنها أسرع طريقة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top