2009-11-04 8 views
59

Je dois déterminer si deux ensembles contiennent exactement les mêmes éléments. La commande n'a pas d'importance.LINQ: Déterminer si deux séquences contiennent exactement les mêmes éléments

Par exemple, ces deux tableaux doivent être considérés comme égaux:

IEnumerable<int> data = new []{ 3,5,6,9 }; 
IEnumerable<int> otherData = new []{ 6,5,9,3} 

Un ensemble ne peut pas contenir des éléments, qui ne sont pas dans l'autre.

Est-ce que cela peut être fait en utilisant les opérateurs de requête intégrés? Et quel serait le moyen le plus efficace de le mettre en œuvre, étant donné que le nombre d'éléments pourrait varier de quelques-uns à des centaines?

+3

Considérez-vous les séquences '{1,1,2}' et '{1,2}' "équivalent"? –

+0

@Mehrdad, Oui, je voudrais que ceux-ci soient considérés égaux. – driis

+0

Par "ensembles", je suppose que vous voulez dire tous les éléments sont uniques? – Kobi

Répondre

100

Si vous voulez traiter les tableaux comme « ensembles » et d'ignorer l'ordre et dupliquer des éléments, vous pouvez utiliser HashSet<T>.SetEquals method:

var isEqual = new HashSet<int>(first).SetEquals(second); 

Sinon, votre meilleur pari est probablement le tri les deux séquences de la même manière et l'utilisation SequenceEqual pour les comparer.

+1

Je pense que HashSet .SetEquals était la méthode que je cherchais :-) – driis

+0

Bonne réponse-- J'ai oublié SetEquals! Si vous avez des dupes, avant le tri, vous devriez probablement copier les séquences dans une liste et comparer d'abord les longueurs, ce qui vous évite les opérations de tri (coûteuses) ou les opérations SequenceEqual() si les longueurs sont différentes. –

+2

@Justin Grant - Je ne suis pas ... Vous devez supprimer les doublons avant de comparer les longueurs, et c'est aussi cher que le tri. – Kobi

-1

Cela devrait aider:

IEnumerable<int> data = new []{ 3,5,6,9 }; 
    IEnumerable<int> otherData = new[] {6, 5, 9, 3}; 

    if(data.All(x => otherData.Contains(x))) 
    { 
     //Code Goes Here 
    } 
+3

C'est O (n²) dans la complexité. Dangereux si vous avez plus de plusieurs dizaines d'articles dans votre liste. –

+0

Simple, mais cela ne fonctionnera pas assez bien pour mon scénario. – driis

+4

Ceci ne sera pas intercepté si 'otherData' contient des éléments supplémentaires. –

3

Si vous pourriez avoir des doublons (ou si vous voulez une solution qui fonctionne mieux pour les listes plus longues), je vais essayer quelque chose comme ceci:

static bool IsSame<T>(IEnumerable<T> set1, IEnumerable<T> set2) 
{ 
    if (set1 == null && set2 == null) 
     return true; 
    if (set1 == null || set2 == null) 
     return false; 

    List<T> list1 = set1.ToList(); 
    List<T> list2 = set2.ToList(); 

    if (list1.Count != list2.Count) 
     return false; 

    list1.Sort(); 
    list2.Sort(); 

    return list1.SequenceEqual(list2); 
} 

MISE À JOUR: oups, vous avez raison, la solution Except() ci-dessous doit regarder dans les deux sens avant de traverser la rue. Et il a minable perf pour les listes plus longues. Ignorez la suggestion ci-dessous! :-)

Voici une façon simple de le faire. Notez que cela suppose que les listes n'ont pas de doublons.

bool same = data.Except (otherData).Count() == 0; 
+3

Vous pourriez utiliser .Any() plutôt que Count() - alors il n'énumera pas tous les éléments de la liste. –

+4

Que faire si 'data = {1,2}, otherData = {1,2,3}'? Vous devriez également vérifier l'inverse. – Kobi

+0

Cela ne fonctionnera pas dans mon scénario, sans vérifier les deux manières comme suggéré par Kobi. Et avec quelques centaines d'éléments, je serais inquiet de la performance pour cette approche. – driis

0
  1. Tout d'abord, vérifier la longueur. Si elles sont différentes, les ensembles sont différents. Vous pouvez faire data.Intersect(otherData);, et vérifiez que la longueur est identique. Ou, simplifiez le tri des ensembles et parcourez-les.
+1

"data.Intersect (otherData), et vérifiez que la longueur est identique" - vous devez vous assurer que la longueur est identique aux deux autres séquences –

+0

@Mark - Au premier pas, vous étiez supposé vérifier qu'ils sont tous les deux de la même longueur , alors ça devrait aller. Ce n'est pas très bien écrit, je suis d'accord. (aussi, parlez de la longue queue ... plus de 2 ans pour obtenir un commentaire ':)') – Kobi

+0

Oui c'est vrai. Vérifier autant de longueur (puisqu'il s'agit de IEnumerable) n'est pas forcément réalisable, bien que comparé à la simple création d'un HashSet à partir de la séquence 1 et à sa comparaison avec la séquence 2. Intersect le fera de toute façon. –

41

Je suggère de trier les deux, et de faire une comparaison élément par élément.

data.OrderBy(x => x).SequenceEqual(otherData.OrderBy(x => x)) 

Je ne suis pas sûr à quelle vitesse la mise en œuvre de OrderBy est, mais si elle est un O (n log n) sorte que vous attendez l'algorithme total est O (n log n) ainsi. Pour certains cas de données, vous pouvez améliorer cela en utilisant une implémentation personnalisée de OrderBy qui utilise par exemple un tri de comptage, pour O (n + k), avec k la taille de la plage dans laquelle se trouvent les valeurs.

-1

Vérifiez d'abord si les deux collectes de données ont le même nombre d'éléments et le contrôle si tous les éléments d'une collection sont présentés dans l'autre

 IEnumerable<int> data = new[] { 3, 5, 6, 9 }; 
     IEnumerable<int> otherData = new[] { 6, 5, 9, 3 }; 

     bool equals = data.Count() == otherData.Count() && data.All(x => otherData.Contains(x)); 
+0

Pour un tableau régulier, .Contains est O (N) et .All est également O (N), ce qui fait de O (N^2). Les options de tri et/ou d'ensemble sont meilleures si votre ensemble de données n'est pas trivialement petit. – Pagefault

+0

Ne donne pas non plus la bonne réponse si les doublons sont autorisés dans l'entrée. – Pagefault

2

Je sais que c'est une vieille question, mais voici une autre façon pour le faire

IEnumerable<int> data = new[] { 3, 5, 6, 9 };    
IEnumerable<int> otherData = new[] { 6, 5, 9, 3 }; 

data = data.OrderBy(d => d); 
otherData = otherData.OrderBy(d => d); 
data.Zip(otherData, (x, y) => Tuple.Create(x, y)).All(d => d.Item1 == d.Item2); 
+1

Votre dernière ligne est essorée. Utilisez 'var isEqual = data.Zip (autreDonnées, (x, y) => x == y) .All (w => w)' à la place;). –

+0

@ shA, t comment est-ce mauvais int == int? Je pourrais voir que c'est un problème avec une classe mais leur ne devrait pas être quelque chose de mal ici –

+0

votre droite J'utilisais une extension interne avant –

Questions connexes