Тестирование равенства словарей в c #

Предполагая, что словарные ключи и значения имеют правильные методы равенства и hashа, каков наиболее краткий и эффективный способ проверки равенства двух словарей?

В этом контексте два словаря называются равными, если они содержат один и тот же набор ключей (порядок не важен), и для каждого такого ключа они соглашаются на значение.

вот несколько способов, с которыми я столкнулся (возможно, еще много):

public bool Compare1( Dictionary dic1, Dictionary dic2) { return dic1.OrderBy(x => x.Key). SequenceEqual(dic2.OrderBy(x => x.Key)); } public bool Compare2( Dictionary dic1, Dictionary dic2) { return (dic1.Count == dic2.Count && dic1.Intersect(dic2).Count(). Equals(dic1.Count)); } public bool Compare3( Dictionary dic1, Dictionary dic2) { return (dic1.Intersect(dic2).Count(). Equals(dic1.Union(dic2).Count())); } 

 dic1.Count == dic2.Count && !dic1.Except(dic2).Any(); 

Это действительно зависит от того, что вы подразумеваете под равенством.

Этот метод будет проверять, что два словаря содержат одни и те же ключи с одинаковыми значениями (при условии, что оба словаря используют ту же реализацию IEqualityComparer ).

 public bool CompareX( Dictionary dict1, Dictionary dict2) { if (dict1 == dict2) return true; if ((dict1 == null) || (dict2 == null)) return false; if (dict1.Count != dict2.Count) return false; var valueComparer = EqualityComparer.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; } 

Вы можете использовать linq для сравнения ключей / значений:

 public bool Compare(Dictionary dict1, Dictionary valueComparer = EqualityComparer.Default; return dict1.Count == dict2.Count && dict1.Keys.All(key => dict2.ContainsKey(key) && valueComparer.Equals(dict1[key], dict2[key])); } 

@ Ответ Аллена :

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

относится к массивам, но поскольку используются методы IEnumerable , его также можно использовать для Dictionary .

Я думал, что принятый ответ будет правильным, основываясь на том, что я читал в smarthelp для метода Except: «Производит заданное различие двух последовательностей, используя сопоставитель равенства по умолчанию для сравнения значений». Но я обнаружил, что это не очень хороший ответ.

Рассмотрим этот код:

 Dictionary> oldDict = new Dictionary>() {{"001A", new List {"John", "Doe"}}, {"002B", new List {"Frank", "Abignale"}}, {"003C", new List {"Doe", "Jane"}}}; Dictionary> newDict = new Dictionary>() {{"001A", new List {"John", "Doe"}}, {"002B", new List {"Frank", "Abignale"}}, {"003C", new List {"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)))))); 

Это приводит к следующему:

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

Как вы можете видеть, оба «oldDict» и «newDict» настроены точно так же. И ни предлагаемое решение, ни призыв к SequenceEqual не работают должным образом. Интересно, является ли это результатом Исключения, используя ленивую загрузку или способ, которым сравнитель устанавливает для Словаря. (Хотя, глядя на структуру и справочные объяснения, предположим, что это должно быть.)

Вот решение, которое я придумал. Обратите внимание, что правило, которое я использовал, выглядит следующим образом: два словаря равны, если оба содержат одинаковые ключи и значения для каждого совпадения ключей. Оба ключа и значения должны быть в одном и том же порядке. И мое решение может быть не самым эффективным, так как оно полагается на итерацию через весь набор ключей.

 private static bool DictionaryEqual( Dictionary> oldDict, Dictionary> 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; } 

Также посмотрите, как изменится результат, если: порядок клавиш не совпадает. (возвращает false)

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

и совпадений в ключе, но значение не соответствует (возвращает false)

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

Если порядок последовательности не имеет значения, функция может быть изменена следующим образом, но, вероятно, будет удар производительности.

 private static bool DictionaryEqual_NoSort( Dictionary> oldDict, Dictionary> 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; } 

Проверьте, не использует ли DictionaryEqual_NoSort следующее для newDict (DictionaryEquals_NoSort возвращает true):

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

Если два словаря содержат одни и те же ключи, но в другом порядке, должны ли они считаться равными? Если нет, то словари должны сравниваться путем запуска счетчиков через оба одновременно. Это, вероятно, будет быстрее, чем перечисление через один словарь и поиск каждого элемента в другом. Если у вас есть предварительное знание о том, что равные словари будут иметь свои элементы в одном порядке, такая двойная перепись, вероятно, является способом выхода.

В вопросах ОП он говорил, что тест равенства должен охватывать не только совпадение ключей, но и их значение. « В этом контексте два словаря считаются равными, если они содержат один и тот же набор ключей (порядок не важен), а для каждый такой ключ, они соглашаются на ценность ».

Я что-то упускаю или отмеченный ответ https://stackoverflow.com/a/3804852/916121 проверяет только равенство размера и ключей, но не их значение?

Я бы разместил это рядом с ответом, но не мог понять, как добавить его в качестве комментария, извините.

Для вложенных словарей и списков я собрал пару идей, чтобы сделать это: https://gist.github.com/NullVoxPopuli/f95baaa48b4e9854dcfe (слишком много кода для отправки здесь) ~ 100 строк

В дополнение к ответу @Nick Jones вам понадобится реализовать gethashcode в том же порядке, что и agnostic. Я бы предложил что-то вроде этого:

 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; } 
Давайте будем гением компьютера.