سؤال

لدي قائمة <> بالكائنات في C# وأحتاج إلى طريقة لإرجاع تلك الكائنات التي تعتبر مكررة داخل القائمة.لا أحتاج إلى مجموعة النتائج المميزة، أحتاج إلى قائمة بتلك العناصر التي سأحذفها من المستودع الخاص بي.

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

Car1.Color = Red;

Car2.Color = Blue;

Car3.Color = Green;

Car4.Color = Red;

Car5.Color = Red;

في هذا المثال، أحتاج إلى النتيجة (IEnumerable<>، أو List<>، أو أيًا كان) التي تحتوي على Car4 وCar5 لأنني أريد حذفها من مستودع التخزين الخاص بي أو قاعدة بياناتي بحيث يكون لدي سيارة واحدة فقط لكل لون في مستودعي.سيكون موضع تقدير أي مساعدة.

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

المحلول

وأنا مشفرة دون قصد هذا بالأمس، عندما كنت أحاول أن أكتب "متميزة عن طريق الإسقاط". I شملت! متى يجب أن يكن لديك، ولكن هذه المرة انها مجرد حق:

public static IEnumerable<TSource> DuplicatesBy<TSource, TKey>
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    HashSet<TKey> seenKeys = new HashSet<TKey>();
    foreach (TSource element in source)
    {
        // Yield it if the key hasn't actually been added - i.e. it
        // was already in the set
        if (!seenKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}

وأنت تريد ثم يطلق عليه مع:

var duplicates = cars.DuplicatesBy(car => car.Color);

نصائح أخرى

var duplicates = from car in cars
                 group car by car.Color into grouped
                 from car in grouped.Skip(1)
                 select car;

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

إذا كان لديك متطلبات معينة حول أي واحد التي تريد الاحتفاظ بها، على سبيل المثال إذا كانت السيارة لديها خاصية Id وتريد للحفاظ على السيارة مع أدنى Id، ثم هل يمكن إضافة بعض الأوامر في هناك، ومنها مثلا.

var duplicates = from car in cars
                 group car by car.Color into grouped
                 from car in grouped.OrderBy(c => c.Id).Skip(1)
                 select car;

وهنا حل ينق مختلف قليلا وأعتقد أن يجعلها أكثر وضوحا ما نحاول القيام به:

var s = from car in cars
    group car by car.Color into g
    where g.Count() == 1
    select g.First();

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

IEnumerable<Car> GetDuplicateColors(List<Car> cars)
{
    return cars.Where(c => cars.Any(c2 => c2.Color == c.Color && cars.IndexOf(c2) < cars.IndexOf(c) ) );
}    

وهذا يعني في الأساس.

"سيارات حيث كان هناك أي سيارة في القائمة مع نفس اللون ومؤشر أصغر العودة"

ولست متأكدا من الأداء، وإن كان. وأظن هذا النهج مع O (1) البحث عن التكرارات (مثل القاموس / طريقة hashset) يمكن أن تكون أسرع لمجموعات كبيرة.

وإنشاء Dictionary<Color, Car> foundColors جديدة وList<Car> carsToDelete

وبعد ذلك يمكنك تكرار خلال القائمة الأصلية للسيارات مثل ذلك:

foreach(Car c in listOfCars)
{
    if (foundColors.containsKey(c.Color))
    {
        carsToDelete.Add(c);
    }
    else
    {
        foundColors.Add(c.Color, c);
    }
}

وبعد ذلك يمكنك حذف كل سيارة وهذا في foundColors.

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

بدون تشفيرها فعليًا، ماذا عن خوارزمية مثل هذا:

  • كرر من خلال الخاص بك List<T> خلق شيء Dictionary<T, int>
  • كرر من خلال الخاص بك Dictionary<T, int> حذف الإدخالات حيث int هو> 1

أي شيء بقي في Dictionary لديه التكرارات.الجزء الثاني الذي تقوم بحذفه فعليًا هو اختياري بالطبع.يمكنك فقط التكرار من خلال Dictionary وابحث عن > 1 لاتخاذ الإجراء.

يحرر:حسنًا، لقد قمت بالتحقق من رايان لأنه أعطاك الكود بالفعل.;)

وجوابي يأخذ الإلهام (في هذا النظام) من أتباع المشاركين: جو Coehoorn، جريج الزان وجون السكيت

وقررت لتوفير مثال كامل، مع افتراض كونه (لكفاءة كلمة حقيقية) أن لديك قائمة ثابتة من الألوان سيارة. وأعتقد أن البرمجية التالية يوضح الحل الكامل لمشكلة في أنيقة، وإن لم يكن بالضرورة في استهلاك المفرط، الطريقة.

#region SearchForNonDistinctMembersInAGenericListSample
public static string[] carColors = new[]{"Red", "Blue", "Green"}; 
public static string[] carStyles = new[]{"Compact", "Sedan", "SUV", "Mini-Van", "Jeep"}; 
public class Car
{
    public Car(){}
    public string Color { get; set; }
    public string Style { get; set; }
}
public static List<Car> SearchForNonDistinctMembersInAList()
{
    // pass in cars normally, but declare here for brevity
    var cars = new List<Car>(5) { new Car(){Color=carColors[0], Style=carStyles[0]}, 
                                      new Car(){Color=carColors[1],Style=carStyles[1]},
                                      new Car(){Color=carColors[0],Style=carStyles[2]}, 
                                      new Car(){Color=carColors[2],Style=carStyles[3]}, 
                                      new Car(){Color=carColors[0],Style=carStyles[4]}};
    List<Car> carDupes = new List<Car>();

    for (int i = 0; i < carColors.Length; i++)
    {
        Func<Car,bool> dupeMatcher = c => c.Color == carColors[i];

        int count = cars.Count<Car>(dupeMatcher);

        if (count > 1) // we have duplicates
        {
            foreach (Car dupe in cars.Where<Car>(dupeMatcher).Skip<Car>(1))
            {
                carDupes.Add(dupe);
            }
        }
    }
    return carDupes;
}
#endregion

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

وساكنة العام IQueryable تكرار (هذا المصدر IEnumerable) حيث TSource: IComparable {

if (source == null)   
     throw new ArgumentNullException("source");   
 return source.Where(x => source.Count(y=>y.Equals(x)) > 1).AsQueryable<TSource>();   

و}

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