Question

Je voudrais comparer le contenu de quelques collections dans ma méthode Equals.J'ai un dictionnaire et une 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 sur les mêmes valeurs, alors ils sont égaux.

Était-ce utile?

La solution

Enumerable.SequenceEqual

Détermine si deux séquences sont égales en comparant leurs éléments à l’aide d’un IEqualityComparer(T) spécifié.

Vous ne pouvez pas comparer directement la liste et le dictionnaire, mais vous pouvez comparer la liste des valeurs du dictionnaire avec la liste

Autres conseils

Comme d'autres l'ont suggéré et noté, SequenceEqual est sensible à la commande.Pour résoudre ce problème, vous pouvez trier le dictionnaire par clé (qui est unique et donc le tri est toujours stable), puis utiliser SequenceEqual.L'expression suivante vérifie si deux dictionnaires sont égaux quel que soit leur ordre interne :

dictionary1.OrderBy(kvp => kvp.Key).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))

MODIFIER: Comme l'a souligné Jeppe Stig Nielsen, certains objets ont une IComparer<T> ce qui est incompatible avec leur IEqualityComparer<T>, donnant des résultats incorrects.Lorsque vous utilisez des clés avec un tel objet, vous devez spécifier un IComparer<T> pour ces clés.Par exemple, avec les clés de chaîne (qui présentent ce problème), vous devez procéder comme suit pour obtenir des résultats corrects :

dictionary1.OrderBy(kvp => kvp.Key, StringComparer.Ordinal).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))

En plus de ce qui est mentionné SéquenceÉgale, lequel

est vrai si deux listes sont de longueur égale et que leurs éléments correspondants se comparent égaux selon un comparateur

(qui peut être le comparateur par défaut, c'est-à-direun remplacé Equals())

il convient de mentionner que dans .Net4 il y a SetEquals sur ISet objets, qui

ignore l'ordre des éléments et les éléments en double.

Donc, si vous souhaitez 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 un HashSet) peut être le bon choix.

Jetez un oeil à Enumerable.SequenceEqual méthode

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);

.NET ne dispose pas 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 permettra de voir 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

Je ne connaissais pas la méthode Enumerable.SequenceEqual (on apprend 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;

    }

Chose intéressante, après avoir pris 2 secondes pour lire SequenceEqual, il semble que Microsoft ait créé la fonction que j'ai décrite pour vous.

Cela ne répond pas directement à vos questions, mais les TestTools et NUnit de MS fournissent tous deux

 CollectionAssert.AreEquivalent

ce qui fait à peu près ce que vous voulez.

Pour comparer des collections, vous pouvez également utiliser LINQ. Enumerable.Intersect renvoie toutes les paires égales.Vous pouvez comparer deux dictionnaires comme ceci :

(dict1.Count == dict2.Count) && dict1.Intersect(dict2).Count() == dict1.Count

La première comparaison est nécessaire car dict2 peut contenir toutes les clés de dict1 et plus.

Vous pouvez également utiliser penser aux variations en utilisant Enumerable.Except et Enumerable.Union qui conduisent à des résultats similaires.Mais peut être utilisé pour déterminer les différences exactes entre les ensembles.

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);
}

Courtoisie : https://www.dotnetperls.com/dictionary-equals

Pour les collections ordonnées (Liste, Tableau), utilisez SequenceEqual

pour une utilisation 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>)

Non.Le cadre de collecte n'a aucune notion d'égalité.Si l’on y réfléchit, il n’existe aucun moyen de comparer les collections qui ne soit subjectif.Par exemple, en comparant votre IList à votre dictionnaire, seraient-ils égaux si toutes les clés étaient dans l'IList, toutes les valeurs étaient dans l'IList ou si les deux étaient dans l'IList ?Il n’existe aucun moyen évident de comparer ces deux collections sans savoir à quoi elles doivent être utilisées, donc une méthode d’égalité à usage général n’a aucun sens.

Non, car le framework ne sait pas comparer le contenu de vos listes.

Jetez un oeil à ceci:

http://blogs.msdn.com/abhinaba/archive/2005/10/11/479537.aspx

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;
}

Il n’y en a pas eu, il n’y en a pas et il se pourrait qu’il n’y en ait pas, du moins je le crois.La raison en 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 naturellement un ordre, 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 pas ?Vous devez le savoir mais je ne sais pas quel est votre point de vue.

Les collections sont conceptuellement désordonnées par défaut, jusqu'à ce que les algorithmes fournissent les règles de tri.La même chose que SQL Server attirera à votre attention est que lorsque vous essayez d'effectuer une 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> n'autorisez 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 n'a donc pas fourni toutes les mises en œuvre possibles.

Eh bien, dans les cas généraux Enumerable.SequenceEqual est suffisant mais renvoie false 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 à plusieurs reprises, sans tenir compte de l'ordre original.Quelques notes de mise en œuvre :

  • GetHashCode() c'est juste pour l'ordre, pas pour l'égalité ;je pense que c'est suffisant dans ce cas

  • Count() n'énumère pas vraiment la collection et relève directement de la mise en œuvre de la propriété de ICollection<T>.Count

  • Si les références sont égales, c'est juste Boris

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top