2011-12-28 12 views
16

mam listę w C#:Czy istnieje metoda AddUnique podobny do Addrange() przez alist w C#

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

problem jest to, że niektóre z tych samych samochodów dostać zwrócone w różnych funkcjach, a ja nie chcesz je na liście więcej niż jeden raz. Każdy samochód ma unikalny atrybut Name. Czy mimo to mogę mieć coś takiego powyżej, ale doda tylko przedmioty, jeśli są unikatowe?

Odpowiedz

13

List<T> nie wydaje się być odpowiednią kolekcję tutaj. Prawdopodobnie potrzebujesz implementacji ISet<T>, takiej jak HashSet<T> (lub SortedSet<T>, jeśli potrzebujesz zamówić).

Aby to umożliwić, należy napisać implementację IEqualityComparer<T>, która definiuje równość między samochodami zgodnie z właściwością Name. Jeśli jest to "kanoniczna" definicja równości samochodów, możesz również rozważyć bezpośrednie zbudowanie tej definicji w samym typie (object.Equals, object.GetHashCode i idealnie zaimplementować również IEquatable<T>).

30

Jeden wybór jest dodać je i usunąć powtarzające się te:

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

mam duplikaty obiektów w mojej kolekcji. Po prostu list = list.Distinct(). ToList(); nie działa. – wotney

+2

Distinct usunie duplikaty obiektów. Jeśli twoje obiekty nie są w rzeczywistości takie same, musisz sprawić, żeby twoje obiekty zaimplementowały Equals() i GetHashCode() – ivowiblo

19

Inną opcją jest zrobić coś takiego:

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()); 
+0

Ładne i proste. – CmdrTallen

3

Inną możliwością użyciu 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()); 
8

myślę, że to jest dość podobna do odpowiedzi Tima Robbinsa.

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

Jeśli chciałbyś rozszerzyć IList, nie rozumiem, dlaczego to nie zadziałałoby.

+0

Nie zapomnij zastąpić Equals() i GetHashCode() w samochodzie lub nie będzie on faktycznie zgodny z obiektem w Contains(). W przeciwnym razie najlepszą odpowiedzią. –

+0

Możesz także zrobić coś takiego, aby porównać konkretny identyfikator wewnątrz obiektu: vm.SearchResults.AddRange (searchResultsOr.Where (listOr =>! SearchResultsAnd.Select (listAnd => listAnd.PostId) .Contains (listOr.PostId))); – Yovav

1

Utworzono metodę rozszerzenia, która dodaje tylko unikalne wartości do niczego implementującego ICollection<T> (w tym List<T>) z IEnumerable<T>. W przeciwieństwie do implementacji, które używają List<T>.Contains(), ta metoda pozwala określić wyrażenie lambda, które określa, czy dwa elementy są takie same.

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

Zastosowanie:

source.AddUniqueBy<Foo>((s, i) => s.Id == i.Id, items); 
0

i jeśli chcesz porównać jedną właściwość (ID w tym przypadku), to powinien pracować

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))); 
Powiązane problemy