Je voudrais comparer le contenu de quelques collections dans ma méthode Equals. J'ai un dictionnaire et un IList. Existe-t-il une méthode intégrée pour ce faire?
Édité: Je veux comparer deux dictionnaires et deux ILists, donc je pense que ce que signifie l'égalité est clair - si les deux dictionnaires contiennent les mêmes clés mappées aux mêmes valeurs, alors elles sont égales.
c#
.net
collections
TimK
la source
la source
IList
? La question est ambiguë.Enumerable.SequenceEqual
etISet.SetEquals
fournissez des versions de cette fonctionnalité. Si vous voulez être indépendant de la commande et travailler avec des collections contenant des doublons, vous devrez créer les vôtres. Découvrez l'implémentation suggérée dans cet articleRéponses:
Enumerable.SequenceEqual
Vous ne pouvez pas comparer directement la liste et le dictionnaire, mais vous pouvez comparer la liste des valeurs du dictionnaire avec la liste
la source
.Keys
et.Values
peut donc renvoyer les clés et les valeurs dans l'ordre de leur choix, et cet ordre est susceptible de changer à mesure que le dictionnaire est également modifié. Je vous suggère de lire ce qu'est un dictionnaire et ce qu'il n'est pas.Comme d'autres l'ont suggéré et noté,
SequenceEqual
est sensible à l'ordre. Pour résoudre cela, vous pouvez trier le dictionnaire par clé (qui est unique, et donc le tri est toujours stable), puis utiliserSequenceEqual
. L'expression suivante vérifie si deux dictionnaires sont égaux quel que soit leur ordre interne:EDIT: Comme l'a souligné Jeppe Stig Nielsen, certains objets ont un
IComparer<T>
qui est incompatible avec le leurIEqualityComparer<T>
, ce qui donne des résultats incorrects. Lorsque vous utilisez des clés avec un tel objet, vous devez spécifier uneIComparer<T>
clé correcte pour ces clés. Par exemple, avec des clés de chaîne (qui présentent ce problème), vous devez effectuer les opérations suivantes pour obtenir des résultats corrects:la source
CompareTo
? Votre solution explosera alors. Et si le type de clé a un comparateur par défaut incompatible avec son comparateur d'égalité par défaut? C'est le casstring
, vous savez. À titre d'exemple, ces dictionnaires (avec des comparateurs d'égalité implicites par défaut) échoueront à votre test (sous toutes les informations de culture dont je suis au courant):var dictionary1 = new Dictionary<string, int> { { "Strasse", 10 }, { "Straße", 20 }, }; var dictionary2 = new Dictionary<string, int> { { "Straße", 20 }, { "Strasse", 10 }, };
IComparer
etIEqualityComparer
- je n'étais pas au courant de ce problème, très intéressant! J'ai mis à jour la réponse avec une solution possible. À propos de l'absence deCompareTo
, je pense que le développeur devrait s'assurer que le délégué fourni à laOrderBy()
méthode renvoie quelque chose de comparable. Je pense que cela est vrai pour toute utilisation ouOrderBy()
, même en dehors des comparaisons de dictionnaires.En plus du SequenceEqual mentionné , qui
(qui peut être le comparateur par défaut, c'est-à-dire un remplacement
Equals()
)il est à noter que dans .Net4 il y a SetEquals sur les
ISet
objets, quiDonc, si vous voulez avoir une liste d'objets, mais qu'ils n'ont pas besoin d'être dans un ordre spécifique, considérez qu'un
ISet
(comme aHashSet
) peut être le bon choix.la source
Jetez un œil à la méthode Enumerable.SequenceEqual
var dictionary = new Dictionary<int, string>() {{1, "a"}, {2, "b"}}; var intList = new List<int> {1, 2}; var stringList = new List<string> {"a", "b"}; var test1 = dictionary.Keys.SequenceEqual(intList); var test2 = dictionary.Values.SequenceEqual(stringList);
la source
.NET Manque d'outils puissants pour comparer les collections. J'ai développé une solution simple que vous pouvez trouver sur le lien ci-dessous:
http://robertbouillon.com/2010/04/29/comparing-collections-in-net/
Cela effectuera une comparaison d'égalité quel que soit l'ordre:
var list1 = new[] { "Bill", "Bob", "Sally" }; var list2 = new[] { "Bob", "Bill", "Sally" }; bool isequal = list1.Compare(list2).IsSame;
Cela vérifiera si des éléments ont été ajoutés / supprimés:
var list1 = new[] { "Billy", "Bob" }; var list2 = new[] { "Bob", "Sally" }; var diff = list1.Compare(list2); var onlyinlist1 = diff.Removed; //Billy var onlyinlist2 = diff.Added; //Sally var inbothlists = diff.Equal; //Bob
Cela verra quels éléments du dictionnaire ont changé:
var original = new Dictionary<int, string>() { { 1, "a" }, { 2, "b" } }; var changed = new Dictionary<int, string>() { { 1, "aaa" }, { 2, "b" } }; var diff = original.Compare(changed, (x, y) => x.Value == y.Value, (x, y) => x.Value == y.Value); foreach (var item in diff.Different) Console.Write("{0} changed to {1}", item.Key.Value, item.Value.Value); //Will output: a changed to aaa
la source
.Removed
est le même quelist1.Except(list2)
,.Added
estlist2.Except(list1)
,.Equal
estlist1.Intersect(list2)
et.Different
estoriginal.Join(changed, left => left.Key, right => right.Key, (left, right) => left.Value == right.Value)
. Vous pouvez faire presque toutes les comparaisons avec LINQ..Different
estoriginal.Join(changed, left => left.Key, right => right.Key, (left, right) => new { Key = left.Key, NewValue = right.Value, Different = left.Value == right.Value).Where(d => d.Different)
. Et vous pouvez même ajouterOldValue = left.Value
si vous avez également besoin de l'ancienne valeur.Je ne connaissais pas la méthode Enumerable.SequenceEqual (vous apprenez quelque chose tous les jours ....), mais j'allais suggérer d'utiliser une méthode d'extension; quelque chose comme ça:
public static bool IsEqual(this List<int> InternalList, List<int> ExternalList) { if (InternalList.Count != ExternalList.Count) { return false; } else { for (int i = 0; i < InternalList.Count; i++) { if (InternalList[i] != ExternalList[i]) return false; } } return true; }
Fait intéressant, après avoir pris 2 secondes pour lire sur SequenceEqual, il semble que Microsoft a créé la fonction que je vous ai décrite.
la source
Cela ne répond pas directement à vos questions, mais les TestTools et NUnit de MS fournissent
qui fait à peu près ce que vous voulez.
la source
Pour comparer des collections, vous pouvez également utiliser LINQ.
Enumerable.Intersect
renvoie toutes les paires égales. Vous pouvez comparer deux dictionnaires comme celui-ci:La première comparaison est nécessaire car
dict2
peut contenir toutes les clés dedict1
et plus.Vous pouvez également utiliser think of variations en utilisant
Enumerable.Except
etEnumerable.Union
qui conduisent à des résultats similaires. Mais peut être utilisé pour déterminer les différences exactes entre les ensembles.la source
Que diriez-vous de cet exemple:
static void Main() { // Create a dictionary and add several elements to it. var dict = new Dictionary<string, int>(); dict.Add("cat", 2); dict.Add("dog", 3); dict.Add("x", 4); // Create another dictionary. var dict2 = new Dictionary<string, int>(); dict2.Add("cat", 2); dict2.Add("dog", 3); dict2.Add("x", 4); // Test for equality. bool equal = false; if (dict.Count == dict2.Count) // Require equal count. { equal = true; foreach (var pair in dict) { int value; if (dict2.TryGetValue(pair.Key, out value)) { // Require value be equal. if (value != pair.Value) { equal = false; break; } } else { // Require key be present. equal = false; break; } } } Console.WriteLine(equal); }
Gracieuseté: https://www.dotnetperls.com/dictionary-equals
la source
Pour les collections ordonnées (List, Array), utilisez
SequenceEqual
pour l'utilisation de HashSet
SetEquals
pour le dictionnaire, vous pouvez faire:
namespace System.Collections.Generic { public static class ExtensionMethods { public static bool DictionaryEquals<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> d1, IReadOnlyDictionary<TKey, TValue> d2) { if (object.ReferenceEquals(d1, d2)) return true; if (d2 is null || d1.Count != d2.Count) return false; foreach (var (d1key, d1value) in d1) { if (!d2.TryGetValue(d1key, out TValue d2value)) return false; if (!d1value.Equals(d2value)) return false; } return true; } } }
(Une solution plus optimisée utilisera le tri mais cela nécessitera
IComparable<TValue>
)la source
Non. Le cadre de collecte n'a aucun concept d'égalité. Si vous y réfléchissez, il n'y a aucun moyen de comparer des collections qui ne soit pas subjective. Par exemple, en comparant votre IList à votre Dictionary, seraient-ils égaux si toutes les clés étaient dans IList, toutes les valeurs étaient dans IList ou si les deux étaient dans IList? Il n'y a pas de moyen évident de comparer ces deux collections sans savoir à quoi elles doivent servir, donc une méthode d'égalité à usage général n'a aucun sens.
la source
Non, car le framework ne sait pas comparer le contenu de vos listes.
Jetez un œil à ceci:
http://blogs.msdn.com/abhinaba/archive/2005/10/11/479537.aspx
la source
IComparer<T>
, Remplaçantobject.Equals
,IEquatable<T>
,IComparable<T>
...public bool CompareStringLists(List<string> list1, List<string> list2) { if (list1.Count != list2.Count) return false; foreach(string item in list1) { if (!list2.Contains(item)) return false; } return true; }
la source
Il n'y en avait pas, il n'y en a pas et peut-être pas, du moins je le croirais. La raison derrière est que l'égalité des collections est probablement un comportement défini par l'utilisateur.
Les éléments des collections ne sont pas censés être dans un ordre particulier bien qu'ils aient un ordre naturellement, ce n'est pas sur quoi les algorithmes de comparaison devraient s'appuyer. Supposons que vous ayez deux collections de:
{1, 2, 3, 4} {4, 3, 2, 1}
Sont-ils égaux ou non? Vous devez savoir mais je ne sais pas quel est votre point de vue.
Les collections sont conceptuellement non ordonnées par défaut, jusqu'à ce que les algorithmes fournissent les règles de tri. La même chose que le serveur SQL portera à votre attention est que lorsque vous essayez de faire la pagination, il vous oblige à fournir des règles de tri:
https://docs.microsoft.com/en-US/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017
Encore deux autres collections:
{1, 2, 3, 4} {1, 1, 1, 2, 2, 3, 4}
Encore une fois, sont-ils égaux ou non? À vous de me dire ..
La répétabilité des éléments d'une collection joue son rôle dans différents scénarios et certaines collections comme
Dictionary<TKey, TValue>
ne permettent même pas les éléments répétés.Je pense que ces types d'égalité sont définis par l'application et que le cadre ne fournit donc pas toutes les implémentations possibles.
Eh bien, dans les cas généraux,
Enumerable.SequenceEqual
c'est assez bon mais il renvoie faux dans le cas suivant:var a = new Dictionary<String, int> { { "2", 2 }, { "1", 1 }, }; var b = new Dictionary<String, int> { { "1", 1 }, { "2", 2 }, }; Debug.Print("{0}", a.SequenceEqual(b)); // false
J'ai lu quelques réponses à des questions comme celle-ci (vous pouvez google pour eux) et ce que j'utiliserais, en général:
public static class CollectionExtensions { public static bool Represents<T>(this IEnumerable<T> first, IEnumerable<T> second) { if(object.ReferenceEquals(first, second)) { return true; } if(first is IOrderedEnumerable<T> && second is IOrderedEnumerable<T>) { return Enumerable.SequenceEqual(first, second); } if(first is ICollection<T> && second is ICollection<T>) { if(first.Count()!=second.Count()) { return false; } } first=first.OrderBy(x => x.GetHashCode()); second=second.OrderBy(x => x.GetHashCode()); return CollectionExtensions.Represents(first, second); } }
Cela signifie qu'une collection représente l'autre dans ses éléments, y compris des temps répétés sans tenir compte de la commande d'origine. Quelques notes de la mise en œuvre:
GetHashCode()
est juste pour l'ordre et non pour l'égalité; Je pense que c'est suffisant dans ce casCount()
n'énumère pas vraiment la collection et tombe directement dans l'implémentation de propriété deICollection<T>.Count
Si les références sont égales, c'est juste Boris
la source
J'ai fait ma propre méthode de comparaison. Il renvoie des valeurs communes, manquantes et supplémentaires.
private static void Compare<T>(IEnumerable<T> actual, IEnumerable<T> expected, out IList<T> common, out IList<T> missing, out IList<T> extra) { common = new List<T>(); missing = new List<T>(); extra = new List<T>(); var expected_ = new LinkedList<T>( expected ); foreach (var item in actual) { if (expected_.Remove( item )) { common.Add( item ); } else { extra.Add( item ); } } foreach (var item in expected_) { missing.Add( item ); } }
la source