@Bul Ikana Działanie tego kodu jest proste, metoda rozszerzenia wywołuje wewnętrznie Equals i GetHashCode metod przesłoniętych klas obiektów, jeśli nie ma elementu IEqualityComparer dla tego zadania.
Mrinal Kamboj
2
Jeśli listy mają długość n i m, jaka jest złożoność czasowa tego algorytmu?
Panik pułkownik
2
Byłoby miło, gdyby sprowadzono to do metody linq o nazwie ContainsAll
Sebastian Patten
60
Jeśli pracujesz z zestawami, użyj HashSet zamiast List. Następnie możesz po prostu użyć IsSubsetOf ()
Dokładnie. Chcesz operacji na zbiorach, użyj klasy przeznaczonej dla nich. Rozwiązanie Camerona jest kreatywne, ale nie tak przejrzyste / wyraziste jak zestaw HashSet.
technophile
2
Nie zgadzam się, ponieważ pytanie konkretnie mówi „użyj LINQ”.
JaredPar
9
@JaredPar: I co z tego? Czy nie lepiej jest pokazać komuś właściwą drogę niż drogę, którą chce iść?
Jonathan Allen
Lista zachowuje swoją kolejność, ale zestaw nie. Jeśli kolejność jest ważna, dałoby to nieprawidłowe wyniki.
Jeśli możesz znaleźć pojedynczy element w t2, który nie jest w t1, to wiesz, że t2 nie jest podzbiorem t1. Zaletą tej metody jest to, że wszystko odbywa się na miejscu, bez przydzielania dodatkowej przestrzeni, w przeciwieństwie do rozwiązań wykorzystujących .Except lub .Intersect. Co więcej, to rozwiązanie może się zepsuć, gdy tylko znajdzie pojedynczy element, który narusza warunek podzbioru, podczas gdy inne kontynuują wyszukiwanie. Poniżej znajduje się optymalna długa forma rozwiązania, która w moich testach jest tylko nieznacznie szybsza niż powyższe rozwiązanie skrócone.
bool isSubset =true;foreach(var element in t2){if(!t1.Contains(element)){
isSubset =false;break;}}
Przeprowadziłem podstawową analizę wydajności wszystkich rozwiązań, a wyniki są drastyczne. Te dwa rozwiązania są około 100 razy szybsze niż rozwiązania .Except () i .Intersect () i nie wymagają dodatkowej pamięci.
To jest dokładnie to, co !t2.Except(t1).Any()robi. Linq pracuje w tę iz powrotem. Any()pyta, IEnumerableczy jest co najmniej jeden element. W tym scenariuszu t2.Except(t1)emituje tylko pierwszy element, t2którego nie ma t1. Jeśli pierwszego elementu t2nie t1ma, kończy się najszybciej, jeśli wszystkie elementy t2są w t1nim, działa najdłużej.
abto
Podczas zabawy z jakiegoś wzorca, I okazało się, jeśli wziąć t1={1,2,3,...9999}i t2={9999,9998,99997...9000}, można uzyskać następujące pomiary: !t2.Except(t1).Any(): 1ms -> t2.All(e => t1.Contains(e)): 702ms. Im większy zakres, tym gorzej.
abto
2
To nie jest sposób, w jaki działa Linq. t2.Except (t1)zwraca a IEnumerablenot a Collection. Emituje wszystkie możliwe elementy tylko wtedy, gdy całkowicie go iterujesz, na przykład przez ToArray ()lub ToList ()lub używasz foreachbez włamywania się do środka. Wyszukaj linq odroczone wykonanie, aby przeczytać więcej o tej koncepcji.
abto
1
Jestem w pełni świadomy tego, jak działa odroczone wykonanie w Linq. Możesz odroczyć wykonanie, ile chcesz, ale jeśli chcesz określić, czy t2 jest podzbiorem t1, musisz powtórzyć całą listę, aby to rozgryźć. Tego faktu nie da się obejść.
user2325458
2
Weźmy przykład z twojego komentarza t2={1,2,3,4,5,6,7,8}t1={2,4,6,8}t2.Except(t1)=> pierwszy element t2 = 1 => różnica od 1 do t1 wynosi 1 (sprawdzane względem {2,4,6,8}) => Except()emituje pierwszy element 1 => Any()pobiera element => Any()daje w wyniku true => brak dalszego sprawdzania elementów w t2.
Opierając się na odpowiedziach z @Cameron i @Neil, napisałem metodę rozszerzającą, która używa tej samej terminologii, co klasa Enumerable.
/// <summary>/// Determines whether a sequence contains the specified elements by using the default equality comparer./// </summary>/// <typeparam name="TSource">The type of the elements of source.</typeparam>/// <param name="source">A sequence in which to locate the values.</param>/// <param name="values">The values to locate in the sequence.</param>/// <returns>true if the source sequence contains elements that have the specified values; otherwise, false.</returns>publicstaticboolContainsAll<TSource>(thisIEnumerable<TSource> source,IEnumerable<TSource> values){return!values.Except(source).Any();}
Tutaj sprawdzamy, czy istnieje element na liście podrzędnej (tj. t2), Który nie jest zawarty w liście nadrzędnej (tj. t1) .Jeśli taki nie istnieje, to lista jest podzbiorem drugiego
Pomysł polega na tym, że Intersect zwróci tylko wartości znajdujące się w obu tablicach. W tym momencie, jeśli długość wynikowego zestawu jest taka sama jak oryginalnego zestawu, to wszystkie elementy w „zestawie” są również w „check” i dlatego „set” jest podzbiorem „toCheck”
Uwaga: moje rozwiązanie nie działa, jeśli „zestaw” ma duplikaty. Nie zmieniam tego, ponieważ nie chcę kraść głosów innych ludzi.
Działa to, jeśli rzeczywiście są zbiorami, ale nie wtedy, gdy drugi „zestaw” zawiera powtarzające się elementy, ponieważ tak naprawdę jest listą. Możesz chcieć użyć HashSet <double>, aby upewnić się, że ma ustawioną semantykę.
tvanfosson
nie działa, gdy obie tablice mają elementy, których nie ma w drugiej tablicy.
Odpowiedzi:
źródło
Jeśli pracujesz z zestawami, użyj HashSet zamiast List. Następnie możesz po prostu użyć IsSubsetOf ()
Przepraszamy, że nie używa LINQ. :-(
Jeśli musisz używać list, rozwiązanie @ Jareda działa z zastrzeżeniem, że będziesz musiał usunąć wszelkie powtarzające się elementy, które istnieją.
źródło
Jeśli wykonujesz testy jednostkowe , możesz również użyć metody CollectionAssert.IsSubsetOf :
W powyższym przypadku oznaczałoby to:
źródło
Jest to znacznie wydajniejsze rozwiązanie niż inne zamieszczone tutaj, zwłaszcza najlepsze rozwiązanie:
Jeśli możesz znaleźć pojedynczy element w t2, który nie jest w t1, to wiesz, że t2 nie jest podzbiorem t1. Zaletą tej metody jest to, że wszystko odbywa się na miejscu, bez przydzielania dodatkowej przestrzeni, w przeciwieństwie do rozwiązań wykorzystujących .Except lub .Intersect. Co więcej, to rozwiązanie może się zepsuć, gdy tylko znajdzie pojedynczy element, który narusza warunek podzbioru, podczas gdy inne kontynuują wyszukiwanie. Poniżej znajduje się optymalna długa forma rozwiązania, która w moich testach jest tylko nieznacznie szybsza niż powyższe rozwiązanie skrócone.
Przeprowadziłem podstawową analizę wydajności wszystkich rozwiązań, a wyniki są drastyczne. Te dwa rozwiązania są około 100 razy szybsze niż rozwiązania .Except () i .Intersect () i nie wymagają dodatkowej pamięci.
źródło
!t2.Except(t1).Any()
robi. Linq pracuje w tę iz powrotem.Any()
pyta,IEnumerable
czy jest co najmniej jeden element. W tym scenariuszut2.Except(t1)
emituje tylko pierwszy element,t2
którego nie mat1
. Jeśli pierwszego elementut2
niet1
ma, kończy się najszybciej, jeśli wszystkie elementyt2
są wt1
nim, działa najdłużej.t1={1,2,3,...9999}
it2={9999,9998,99997...9000}
, można uzyskać następujące pomiary:!t2.Except(t1).Any(): 1ms -> t2.All(e => t1.Contains(e)): 702ms
. Im większy zakres, tym gorzej.t2.Except (t1)
zwraca aIEnumerable
not aCollection
. Emituje wszystkie możliwe elementy tylko wtedy, gdy całkowicie go iterujesz, na przykład przezToArray ()
lubToList ()
lub używaszforeach
bez włamywania się do środka. Wyszukaj linq odroczone wykonanie, aby przeczytać więcej o tej koncepcji.t2={1,2,3,4,5,6,7,8}
t1={2,4,6,8}
t2.Except(t1)
=> pierwszy element t2 = 1 => różnica od 1 do t1 wynosi 1 (sprawdzane względem {2,4,6,8}) =>Except()
emituje pierwszy element 1 =>Any()
pobiera element =>Any()
daje w wyniku true => brak dalszego sprawdzania elementów w t2.Rozwiązanie @ Camerona jako metoda rozszerzenia:
Stosowanie:
(Jest podobny, ale nie taki sam jak ten opublikowany na blogu @ Michael)
źródło
Opierając się na odpowiedziach z @Cameron i @Neil, napisałem metodę rozszerzającą, która używa tej samej terminologii, co klasa Enumerable.
źródło
na przykład:
źródło
Spróbuj tego
Pomysł polega na tym, że Intersect zwróci tylko wartości znajdujące się w obu tablicach. W tym momencie, jeśli długość wynikowego zestawu jest taka sama jak oryginalnego zestawu, to wszystkie elementy w „zestawie” są również w „check” i dlatego „set” jest podzbiorem „toCheck”
Uwaga: moje rozwiązanie nie działa, jeśli „zestaw” ma duplikaty. Nie zmieniam tego, ponieważ nie chcę kraść głosów innych ludzi.
Podpowiedź: głosowałem za odpowiedzią Camerona.
źródło