288 votes

Comparer deux listes <T> objets pour l'égalité, en ignorant l'ordre

Encore un autre à la liste de comparaison question.

List<MyType> list1;
List<MyType> list2;

J'ai besoin de vérifier qu'ils ont les mêmes éléments, quelle que soit leur position dans la liste. Chaque MyType objet peut apparaître plusieurs fois sur une liste. Est-il intégré dans la fonction qui vérifie ce? Que faire si j'ai la garantie que chaque élément n'apparaît qu'une fois dans une liste?

EDIT: les Gars merci pour les réponses, mais j'ai oublié d'ajouter quelque chose, le nombre d'occurrences de chaque élément doit être la même sur les deux listes.

366voto

Guffa Points 308133

Si vous voulez qu'ils soient vraiment égaux (c'est à dire les mêmes produits et le même nombre de chaque élément), je pense que la solution la plus simple est de les trier avant de les comparer:

Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))

Edit:

Voici une solution qui effectue un peu mieux (environ dix fois plus rapide), et ne nécessite qu' IEquatable, pas IComparable:

public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2) {
  var cnt = new Dictionary<T, int>();
  foreach (T s in list1) {
    if (cnt.ContainsKey(s)) {
      cnt[s]++;
    } else {
      cnt.Add(s, 1);
    }
  }
  foreach (T s in list2) {
    if (cnt.ContainsKey(s)) {
      cnt[s]--;
    } else {
      return false;
    }
  }
  return cnt.Values.All(c => c == 0);
}

Edit 2:

Pour gérer tout type de données clés (par exemple des types nullables comme Frank Tzanabetis souligné), vous pouvez faire une version qui prend un comparateur pour le dictionnaire:

public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
  var cnt = new Dictionary<T, int>(comparer);
  ...

54voto

recursive Points 34729

Si vous ne vous souciez pas du nombre d'occurrences, je l'approcherais comme ceci. L'utilisation de jeux de hachage vous donnera de meilleures performances que la simple itération.

 var set1 = new HashSet<MyType>(list1);
var set2 = new HashSet<MyType>(list2);
return set1.SetEquals(set2);
 

Cela nécessitera que vous ayez remplacé .GetHashCode() et IEquatable<MyType> sur MyType .

49voto

LBushkin Points 60611

Comme l'écrit, cette question est ambiguë. La déclaration:

... ils ont tous deux les mêmes éléments, quelle que soit leur position dans la liste. Chaque MyType objet peut apparaître plusieurs fois sur une liste.

ne pas indiquer si vous voulez vous assurer que les deux listes ont le même ensemble d'objets ou le même ensemble distinct.

Si vous voulez assurer aux collections ont exactement le même ensemble de membres, quel que soit l'ordre, vous pouvez utiliser:

// lists should have same count of items, and set difference must be empty
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();

Si vous voulez vous assurer que les deux collections ont le même ensemble distinct de membres (d'où les doublons dans les deux sont ignorés), vous pouvez utiliser:

// check that [(A-B) Union (B-A)] is empty
var areEquivalent = !list1.Except(list2).Union( list2.Except(list1) ).Any();

En utilisant l'ensemble des opérations (Intersect, Union, Except) est plus efficace que l'utilisation de méthodes comme l' Contains. À mon avis, il est aussi mieux exprime les attentes de votre requête.

EDIT: Maintenant que vous avez précisé votre question, je peux dire que vous voulez utiliser la première forme - depuis les doublons question. Voici un exemple simple pour montrer que vous obtenez le résultat que vous souhaitez:

var a = new[] {1, 2, 3, 4, 4, 3, 1, 1, 2};
var b = new[] { 4, 3, 2, 3, 1, 1, 1, 4, 2 };

// result below should be true, since the two sets are equivalent...
var areEquivalent = (a.Count() == b.Count()) && !a.Except(b).Any(); 

10voto

Brian Genisio Points 30777

Pensant cela devrait faire ce que vous voulez :

Si vous voulez qu’elle soit distincte, vous pouvez le remplacer pour :

9voto

Ani Points 59747

C'est un peu difficile problème, qui je pense se réduit à: "Tester si deux listes sont des permutations de chaque autre."

Je crois que les solutions fournies par d'autres seulement indiquer si les 2 listes contiennent les mêmes unique des éléments. C'est une condition nécessaire mais pas suffisante de test, par exemple {1, 1, 2, 3} n'est pas une permutation de l' {3, 3, 1, 2} bien que leurs nombres sont égaux et qu'ils contiennent les mêmes éléments distincts.

Je crois que cela devrait fonctionner, cependant, bien que ce n'est pas le plus efficace:

static bool ArePermutations<T>(IList<T> list1, IList<T> list2)
{
   if(list1.Count != list2.Count)
         return false;

   var l1 = list1.ToLookup(t => t);
   var l2 = list2.ToLookup(t => t);

   return l1.Count == l2.Count 
       && l1.All(group => l2.Contains(group.Key) && l2[group.Key].Count() == group.Count()); 
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X