Подтвердить что ты не робот

С# 3.0: нужно возвращать дубликаты из списка <>

У меня есть List < > объектов в С#, и мне нужен способ вернуть те объекты, которые считаются дублирующими в списке. Мне не нужен набор результатов Distinct, мне нужен список тех элементов, которые я удалю из своего репозитория.

Для этого примера, скажем, у меня есть список типов "Автомобиль", и мне нужно знать, какие из этих автомобилей имеют тот же цвет, что и другой в списке. Вот автомобили в списке и их свойство цвета:

Car1.Color = Red;

Car2.Color = Blue;

Car3.Color = Green;

Car4.Color = Red;

Car5.Color = Red;

Для этого примера мне нужен результат (IEnumerable < > , List < > или что-то еще), чтобы содержать Car4 и Car5, потому что я хочу удалить их из своего репозитория или db, чтобы у меня был только один автомобиль за цвет в моем репозитории, Любая помощь будет оценена.

4b9b3361

Ответ 1

Я нечаянно закодировал это вчера, когда я пытался написать "отличную от проекции". Я включил! когда я не должен был, но на этот раз это правильно:

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

Ответ 2

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;

Ответ 3

Здесь немного другое решение Linq, которое, я думаю, делает более очевидным то, что вы пытаетесь сделать:

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

Он просто группирует автомобили по цвету, выкидывая все группы с более чем одним элементом, а затем помещая остальные в возвращаемый IEnumerable.

Ответ 4

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) поиском дубликатов (например, метод dictionary/hashset) может быть быстрее для больших наборов.

Ответ 5

Создайте новый 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 вместо создания нового списка, но так, как вы сформулировали вопрос, предложили, чтобы вам нужно было собрать их в списке.

Ответ 6

Без его кодирования, как насчет алгоритма примерно так:

  • итерации через List<T> создание Dictionary<T, int>
  • итерации через Dictionary<T, int> удаление записей, в которых int есть > 1

Все, что осталось в Dictionary, имеет дубликаты. Конечно, вторая часть, где вы фактически удаляете, является необязательной. Вы можете просто прокручивать через Dictionary и искать > 1, чтобы принять меры.

EDIT: Хорошо, я натолкнулся на Райана, так как он действительно дал вам код.;)

Ответ 7

Мой ответ берет вдохновение (в этом порядке) от респондентов-последователей: Джо Кохорн, Грег Бич и Джон Скит.

Я решил представить полный пример, исходя из предположения (для реального эффективного использования слова), что у вас есть статический список цветов автомобилей. Я считаю, что следующий код иллюстрирует полное решение проблемы в элегантной, хотя и не обязательно гиперэффективной манере.

#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

Я собираюсь вернуться сюда позже и сравнить это решение со всеми тремя его вдохновениями, просто чтобы сравнить стили. Это довольно интересно.

Ответ 8

public static IQueryable Duplicates (этот источник IEnumerable), где TSource: IComparable {

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

}