Есть ли метод AddUnique, аналогичный Addrange () для alist в C # - PullRequest
25 голосов
/ 28 декабря 2011

У меня есть список в C #:

       var list = new List<Car>();
       list.AddRange(GetGreenCars());
       list.AddRange(GetBigCars());
       list.AddRange(GetSmallCars());

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

Ответы [ 9 ]

42 голосов
/ 28 декабря 2011

Один из вариантов - добавить их и удалить повторяющиеся:

var list = new List<Car>();
list.AddRange(GetGreenCars());
list.AddRange(GetBigCars());
list.AddRange(GetSmallCars());
list = list.Distinct().ToList();
23 голосов
/ 28 декабря 2011

Другой вариант - сделать что-то вроде:

public static void AddUnique<T>( this IList<T> self, IEnumerable<T> items )
{
    foreach(var item in items)
        if(!self.Contains(item))
            self.Add(item);
}


var list = new List<Car>();
list.AddUnique(GetGreenCars());
list.AddUnique(GetBigCars());
list.AddUnique(GetSmallCars());
16 голосов
/ 28 декабря 2011

A List<T> не представляется подходящей коллекцией здесь.Возможно, вам нужна реализация ISet<T>, такая как HashSet<T> (или SortedSet<T>, если вам нужен заказ).

Чтобы разрешить это, вам потребуетсянаписать реализацию IEqualityComparer<T>, которая определяет равенство между автомобилями согласно свойству Name.Если это «каноническое» определение равенства автомобилей, вы также можете рассмотреть возможность непосредственного встраивания этого определения в сам тип Car (object.Equals, object.GetHashCode и в идеале также реализовать IEquatable<T>).

10 голосов
/ 14 июля 2015

Если вы переопределите метод .Equals() для Car, чтобы определить, что один автомобильный объект совпадает с другим автомобильным объектом, то следующее должно работать без написания метода расширения.

    var list = new List<Car>();
    list.AddRange(GetGreenCars()?.Except(list));
    list.AddRange(GetBigCars()?.Except(list));
    list.AddRange(GetSmallCars()?.Except(list));
5 голосов
/ 21 февраля 2015

Еще один вариант с использованием Linq:

public static void AddUnique<T>(this IList<T> self, IEnumerable<T> items)
{
  self.AddRange(
    items.Where(x => self.FirstOrDefault(y => y.Name == x.Name) ==
    null).ToList());
}

var list = new List<Car>();
list.AddUnique(GetGreenCars());
list.AddUnique(GetBigCars());
list.AddUnique(GetSmallCars());
3 голосов
/ 05 октября 2015

Я создал метод расширения, который добавляет только уникальные значения ко всему, что реализует ICollection<T> (включая List<T>) из IEnumerable<T>. В отличие от реализаций, которые используют List<T>.Contains(), этот метод позволяет вам указать лямбда-выражение, которое определяет, являются ли два элемента одинаковыми.

/// <summary>
/// Adds only items that do not exist in source.  May be very slow for large collections and some types of source.
/// </summary>
/// <typeparam name="T">Type in the collection.</typeparam>
/// <param name="source">Source collection</param>
/// <param name="predicate">Predicate to determine whether a new item is already in source.</param>
/// <param name="items">New items.</param>
public static void AddUniqueBy<T>(this ICollection<T> source, Func<T, T, bool> predicate, IEnumerable<T> items)
{
    foreach (T item in items)
    {
        bool existsInSource = source.Where(s => predicate(s, item)).Any();
        if (!existsInSource) source.Add(item);
    }
}

Использование:

source.AddUniqueBy<Foo>((s, i) => s.Id == i.Id, items);
1 голос
/ 20 марта 2019

При условии, что ваши Get * Cars () возвращают Lists of Car, другой вариант может быть:

var list = new List<Car>();
GetGreenCars().ForEach(c => { if (!list.Contains(c)) list.Add(c); });
GetBigCars().ForEach(c => { if (!list.Contains(c)) list.Add(c); });
GetSmallCars().ForEach(c => { if (!list.Contains(c)) list.Add(c); });
1 голос
/ 18 мая 2018

Другой вариант, если программист не может или не хочет использовать Linq.

var list = new List<Car>();
list.AddRange(GetGreenCars().FindAll((x) => !list.Contains(x)));
list.AddRange(GetBigCars().FindAll((x) => !list.Contains(x)));
list.AddRange(GetSmallCars().FindAll((x) => !list.Contains(x)));

Если начальный список пуст, как в примере выше, вы можете на самом деле избежать использования FindAll (...) в первом AddRange ().

0 голосов
/ 26 апреля 2016

и если вы хотите сравнить одно свойство (в данном случае идентификатор), это должно работать

var list = new List<string>();
list.AddRange(GetGreenCars().Where(greencar => !list.Contains(greencar, car => car.id == greencar.id)));
list.AddRange(GetBigCars().Where(bigcar => !list.Contains(bigcar, car => car.id == bigcar.id)));
list.AddRange(GetSmallCars().Where(smallcar => !list.Contains(smallcar, car => car.id == smallcar.id)));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...