Masz pomysł, jak sprawdzić, czy ta lista jest podzbiorem innej listy?
W szczególności mam
List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };
Jak sprawdzić, czy t2 jest podzbiorem t1, używając LINQ?
Masz pomysł, jak sprawdzić, czy ta lista jest podzbiorem innej listy?
W szczególności mam
List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };
Jak sprawdzić, czy t2 jest podzbiorem t1, używając LINQ?
Odpowiedzi:
bool isSubset = !t2.Except(t1).Any();
Jeśli pracujesz z zestawami, użyj HashSet zamiast List. Następnie możesz po prostu użyć IsSubsetOf ()
HashSet<double> t1 = new HashSet<double>{1,3,5};
HashSet<double> t2 = new HashSet<double>{1,5};
bool isSubset = t2.IsSubsetOf(t1);
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ą.
Jeśli wykonujesz testy jednostkowe , możesz również użyć metody CollectionAssert.IsSubsetOf :
CollectionAssert.IsSubsetOf(subset, superset);
W powyższym przypadku oznaczałoby to:
CollectionAssert.IsSubsetOf(t2, t1);
Jest to znacznie wydajniejsze rozwiązanie niż inne zamieszczone tutaj, zwłaszcza najlepsze rozwiązanie:
bool isSubset = t2.All(elem => t1.Contains(elem));
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.
!t2.Except(t1).Any()
robi. Linq pracuje w tę iz powrotem. Any()
pyta, IEnumerable
czy jest co najmniej jeden element. W tym scenariuszu t2.Except(t1)
emituje tylko pierwszy element, t2
którego nie ma t1
. Jeśli pierwszego elementu t2
nie t1
ma, kończy się najszybciej, jeśli wszystkie elementy t2
są w t1
nim, działa najdłużej.
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.
t2.Except (t1)
zwraca a IEnumerable
not a Collection
. Emituje wszystkie możliwe elementy tylko wtedy, gdy całkowicie go iterujesz, na przykład przez ToArray ()
lub ToList ()
lub używasz foreach
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.
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>
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> values)
{
return !values.Except(source).Any();
}
Spróbuj tego
static bool IsSubSet<A>(A[] set, A[] toCheck) {
return set.Length == (toCheck.Intersect(set)).Count();
}
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.