Есть ли метод AddUnique, аналогичный Addrange() для alist в С#
У меня есть список в С#:
var list = new List<Car>();
list.AddRange(GetGreenCars());
list.AddRange(GetBigCars());
list.AddRange(GetSmallCars());
проблема в том, что некоторые из тех же автомобилей возвращаются в разных функциях, и я не хочу их в списке более одного раза. Каждый автомобиль имеет уникальный атрибут Name. В любом случае, я могу иметь что-то подобное выше, но будет добавлять только элементы, если они уникальны?
Ответы
Ответ 1
A List<T>
, похоже, не является подходящей коллекцией. Вероятно, вам нужна реализация ISet<T>
, например HashSet<T>
(или SortedSet<T>
, если вам нужно заказать).
Чтобы это разрешить, вам нужно написать реализацию IEqualityComparer<T>
, которая определяет равенство между машинами в соответствии с свойством Name
. Если это "каноническое" определение car-равенства, вы можете также прямо построить это определение в самом типе Car
(object.Equals
, object.GetHashCode
и в идеале реализовать IEquatable<T>
).
Ответ 2
Один из них - добавить их и удалить повторяющиеся:
var list = new List<Car>();
list.AddRange(GetGreenCars());
list.AddRange(GetBigCars());
list.AddRange(GetSmallCars());
list = list.Distinct().ToList();
Ответ 3
Другой вариант - сделать что-то вроде:
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());
Ответ 4
Если вы переопределяете метод .Equals()
для Car
, чтобы определить, что один автомобильный объект совпадает с другим автомобильным объектом, то следующее должно работать без написания метода расширения.
var list = new List<Car>();
list.AddRange(GetGreenCars()?.Except(list) ?? new List<Car>());
list.AddRange(GetBigCars()?.Except(list) ?? new List<Car>());
list.AddRange(GetSmallCars()?.Except(list) ?? new List<Car>());
Ответ 5
Еще один вариант использования 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());
Ответ 6
Я создал метод расширения, который добавляет только уникальные значения для чего-либо, реализующего 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);
Ответ 7
Другой вариант, если программист не может или не хочет использовать 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().
Ответ 8
Предполагая, что ваши 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); });
Ответ 9
и если вы хотите сравнить одно свойство (id в этом случае), это должно работать
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)));