Вопрос

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

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

Car1.Color = Red;

Car2.Color = Blue;

Car3.Color = Green;

Car4.Color = Red;

Car5.Color = Red;

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

Это было полезно?

Решение

Я непреднамеренно закодировал это вчера, когда пытался написать "distinct by a projection".Я включил !когда я не должен был этого делать, но на этот раз все в самый раз:

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;

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

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

Создайте новый Dictionary<Color, Car> foundColors и a 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, чтобы предпринять действия.

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

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

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

#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 :Несравнимый {

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