web-dev-qa-db-fra.com

Vérifier si un tableau est un sous-ensemble d'un autre

Une idée sur la façon de vérifier si cette liste est un sous-ensemble d'un autre?

Plus précisément, j'ai

List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };

Comment vérifier que t2 est un sous-ensemble de t1, en utilisant LINQ?

131
Graviton
bool isSubset = !t2.Except(t1).Any();
235
Cameron MacFarland

Utilisez HashSet au lieu de List si vous utilisez des ensembles. Ensuite, vous pouvez simplement utiliser IsSubsetOf ()

HashSet<double> t1 = new HashSet<double>{1,3,5};
HashSet<double> t2 = new HashSet<double>{1,5};

bool isSubset = t2.IsSubsetOf(t1);

Désolé de ne pas utiliser LINQ. :-(

Si vous avez besoin d’utiliser des listes, la solution de @ Jared fonctionne avec l’avertissement selon laquelle vous devrez supprimer tout élément répété existant.

52
tvanfosson

Si vous êtes unit-testing, vous pouvez également utiliser la méthode CollectionAssert.IsSubsetOf :

CollectionAssert.IsSubsetOf(subset, superset);

Dans le cas ci-dessus, cela signifierait:

CollectionAssert.IsSubsetOf(t2, t1);
9
Géza

La solution de Cameron en tant que méthode d'extension:

public static bool IsSubsetOf<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
    return !a.Except(b).Any();
}

Usage:

bool isSubset = t2.IsSubsetOf(t1);

(Ceci est similaire, mais pas tout à fait le même que celui posté sur le blog de @ Michael)

6
Neil

C'est une solution nettement plus efficace que les autres publiées ici, en particulier la solution la plus performante:

bool isSubset = t2.All(elem => t1.Contains(elem));

Si vous pouvez trouver un seul élément dans t2 qui ne soit pas dans t1, alors vous savez que t2 n'est pas un sous-ensemble de t1. L'avantage de cette méthode est que toutes les opérations sont effectuées sur place, sans allouer d'espace supplémentaire, contrairement aux solutions utilisant .Except ou .Intersect. De plus, cette solution est capable de casser dès qu'elle trouve un seul élément qui viole la condition de sous-ensemble, tandis que les autres continuent la recherche. Vous trouverez ci-dessous la forme longue optimale de la solution, qui n’est guère plus rapide dans mes tests que la solution abrégée ci-dessus.

bool isSubset = true;
foreach (var element in t2) {
    if (!t1.Contains(element)) {
        isSubset = false;
        break;
    }
}

J'ai effectué une analyse de performance rudimentaire de toutes les solutions et les résultats sont drastiques. Ces deux solutions sont environ 100 fois plus rapides que les solutions .Except () et .Intersect () et n'utilisent aucune mémoire supplémentaire. 

5
user2325458

En me basant sur les réponses de @Cameron et de @Neil, j'ai écrit une méthode d'extension qui utilise la même terminologie que la classe 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();
}
0
sclarke81

Nous vérifions ici que s’il existe un élément dans la liste des enfants (i.e t2) qui n’est pas contenu dans la liste des parents (i.e t1). S'il n'en existe aucun, la liste est un sous-ensemble de

par exemple:

bool isSubset = !(t2.Any(x => !t1.Contains(x)));
0
Lucifer