web-dev-qa-db-fra.com

Test d'égalité entre les dictionnaires en c #

En supposant que les clés et les valeurs du dictionnaire aient leurs méthodes d'égalité et de hachage correctement implémentées, quel est le moyen le plus succinct et le plus efficace de tester l'égalité entre deux dictionnaires?

Dans ce contexte, deux dictionnaires sont dits égaux s'ils contiennent le même jeu de clés (ordre sans importance), et pour chaque clé de ce type, ils s'accordent sur la valeur.

voici quelques façons dont je suis venu (il y en a probablement beaucoup plus):

public bool Compare1<TKey, TValue>(
    Dictionary<TKey, TValue> dic1, 
    Dictionary<TKey,TValue> dic2)
{
    return dic1.OrderBy(x => x.Key).
        SequenceEqual(dic2.OrderBy(x => x.Key));
}

public bool Compare2<TKey, TValue>(
    Dictionary<TKey, TValue> dic1, 
    Dictionary<TKey, TValue> dic2)
{
    return (dic1.Count == dic2.Count && 
        dic1.Intersect(dic2).Count().
        Equals(dic1.Count));
}

public bool Compare3<TKey, TValue>(
    Dictionary<TKey, TValue> dic1, 
    Dictionary<TKey, TValue> dic2)
{
    return (dic1.Intersect(dic2).Count().
        Equals(dic1.Union(dic2).Count()));
}
51
rony l
dic1.Count == dic2.Count && !dic1.Except(dic2).Any();
86
Nick Jones

Cela dépend vraiment de ce que vous entendez par égalité.

Cette méthode testera que deux dictionnaires contiennent les mêmes clés avec les mêmes valeurs (en supposant que les deux dictionnaires utilisent la même implémentation IEqualityComparer<TKey>).

public bool CompareX<TKey, TValue>(
    Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue> dict2)
{
    if (dict1 == dict2) return true;
    if ((dict1 == null) || (dict2 == null)) return false;
    if (dict1.Count != dict2.Count) return false;

    var valueComparer = EqualityComparer<TValue>.Default;

    foreach (var kvp in dict1)
    {
        TValue value2;
        if (!dict2.TryGetValue(kvp.Key, out value2)) return false;
        if (!valueComparer.Equals(kvp.Value, value2)) return false;
    }
    return true;
}
12
LukeH

Vous pouvez utiliser linq pour les comparaisons clé/valeur:

public bool Compare<TKey, TValue>(Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue dict2)
{
    IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.Default;

    return  dict1.Count == dict2.Count &&
            dict1.Keys.All(key => dict2.ContainsKey(key) && valueComparer.Equals(dict1[key], dict2[key]));
}
3
Lee

La réponse de @ Allen :

bool equals = a.Intersect(b).Count() == a.Union(b).Count()

concerne les tableaux, mais dans la mesure où les méthodes IEnumerable<T> sont utilisées, elles peuvent aussi être utilisées pour Dictionary<K,V>.

1
abatishchev

Je pensais que la réponse acceptée serait correcte sur la base de ce que je lisais dans le smarthelp pour la méthode Except: "Produit la différence définie de deux séquences en utilisant le comparateur d’égalité par défaut pour comparer les valeurs". Mais j'ai découvert que ce n'était pas une bonne réponse.

Considérons ce code:

Dictionary<string, List<string>> oldDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"002B", new List<string> {"Frank", "Abignale"}},
     {"003C", new List<string> {"Doe", "Jane"}}};
Dictionary<string, List<string>> newDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"002B", new List<string> {"Frank", "Abignale"}},
     {"003C", new List<string> {"Doe", "Jane"}}};

bool equal = oldDict.Count.Equals(newDict.Count) && !oldDict.Except(newDict).Any();
Console.WriteLine(string.Format("oldDict {0} newDict", equal?"equals":"does not equal"));
equal = oldDict.SequenceEqual(newDict);
Console.WriteLine(string.Format("oldDict {0} newDict", equal ? "equals" : "does not equal"));

Console.WriteLine(string.Format("[{0}]", string.Join(", ", 
    oldDict.Except(newDict).Select(k => 
        string.Format("{0}=[{1}]", k.Key, string.Join(", ", k.Value))))));

Cela se traduit par:

oldDict does not equal newDict
oldDict does not equal newDict
[001A=[John, Doe], 002B=[Frank, Abignale], 003C=[Doe, Jane]]

Comme vous pouvez le constater, "oldDict" et "newDict" ont exactement la même configuration. Et ni la solution proposée ni un appel à SequenceEqual ne fonctionnent correctement. Je me demande s’il s’agit du résultat de la commande Sauf à l’utilisation du chargement différé ou de la façon dont le comparateur est configuré pour le dictionnaire. (Bien que, en regardant la structure et les références, les explications suggèrent qu'il le devrait.)

Voici la solution que j'ai trouvée. Notez que la règle que j'ai utilisée est la suivante: deux dictionnaires sont égaux si les deux contiennent les mêmes clés et les valeurs pour chaque clé correspondent. Les clés et les valeurs doivent être dans le même ordre séquentiel. Et ma solution n’est peut-être pas la plus efficace, car elle repose sur l’itération de l’ensemble des clés. 

private static bool DictionaryEqual(
    Dictionary<string, List<string>> oldDict, 
    Dictionary<string, List<string>> newDict)
{
    // Simple check, are the counts the same?
    if (!oldDict.Count.Equals(newDict.Count)) return false;

    // Verify the keys
    if (!oldDict.Keys.SequenceEqual(newDict.Keys)) return false;

    // Verify the values for each key
    foreach (string key in oldDict.Keys)
        if (!oldDict[key].SequenceEqual(newDict[key]))
            return false;

    return true;
}

Voyez également comment les résultats changent si: L'ordre des clés n'est pas le même. (retourne faux)

newDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"003C", new List<string> {"Doe", "Jane"}},
     {"002B", new List<string> {"Frank", "Abignale"}}};

et La commande de clé correspond, mais la valeur ne correspond pas (renvoie false)

newDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"002B", new List<string> {"Frank", "Abignale"}},
     {"003C", new List<string> {"Jane", "Doe"}}};

Si l'ordre de la séquence n'a pas d'importance, la fonction peut être modifiée comme suit, mais il y a probablement un impact négatif sur les performances.

private static bool DictionaryEqual_NoSort(
    Dictionary<string, List<string>> oldDict,
    Dictionary<string, List<string>> newDict)
{
    // Simple check, are the counts the same?
    if (!oldDict.Count.Equals(newDict.Count)) return false;

    // iterate through all the keys in oldDict and
    // verify whether the key exists in the newDict
    foreach(string key in oldDict.Keys)
    {
        if (newDict.Keys.Contains(key))
        {
            // iterate through each value for the current key in oldDict and 
            // verify whether or not it exists for the current key in the newDict
            foreach(string value in oldDict[key])
                if (!newDict[key].Contains(value)) return false;
        }
        else { return false; }
    }

    return true;
}

Vérifiez si DictionaryEqual_NoSort utilise les éléments suivants pour newDict (DictionaryEquals_NoSort renvoie true):

newDict = new Dictionary<string, List<string>>()
    {{"001A", new List<string> {"John", "Doe"}},
     {"003C", new List<string> {"Jane", "Doe"}},
     {"002B", new List<string> {"Frank", "Abignale"}}};     
1
Machtyn

En plus de la réponse de @Nick Jones, vous allez devoir implémenter gethashcode de la même manière, en agnostic order. Je suggérerais quelque chose comme ça:

public override int GetHashCode()
{
        int hash = 13;
        var orderedKVPList = this.DictProp.OrderBy(kvp => kvp.key)
        foreach (var kvp in orderedKVPList)
        {
                 hash = (hash * 7)  + kvp.Key.GetHashCode();
                 hash = (hash * 7)  + kvp.value.GetHashCode();
        }
        return hash;
}
1
Jason Masters

Si deux dictionnaires contiennent les mêmes clés, mais dans un ordre différent, doivent-ils être considérés comme égaux? Si ce n'est pas le cas, les dictionnaires doivent être comparés en exécutant simultanément des énumérateurs à la fois. Ce sera probablement plus rapide que d'énumérer dans un dictionnaire et de rechercher chaque élément dans l'autre. Si vous savez à l'avance que des dictionnaires identiques auront leurs éléments dans le même ordre, une telle double énumération est probablement la voie à suivre.

0
supercat

Dans les questions OP, il était indiqué que le test d’égalité devait couvrir non seulement la correspondance des clés, mais également leur valeur "Dans ce contexte, deux dictionnaires sont dits égaux s’ils contiennent le même jeu de clés (ordre non important) et pour chacune de ces clés, ils s'accordent sur la valeur. "

Est-ce que je manque quelque chose ou est-ce que la réponse marquée https://stackoverflow.com/a/3804852/916121 vérifie uniquement l'égalité de taille et de clé mais pas leur valeur?

J'aurais posté ceci à côté de la réponse, mais je n'ai pas trouvé comment l'ajouter comme commentaire, désolé.

0
Access IT

Pour les dictionnaires et les listes imbriqués, j’ai combiné quelques idées ici: https://Gist.github.com/NullVoxPopuli/f95baaa48b4e9854dcfe (trop de code à publier ici) ~ 100

0
NullVoxPopuli