Ответ 1
dic1.Count == dic2.Count && !dic1.Except(dic2).Any();
Предполагая, что словарные ключи и значения имеют правильные методы равенства и хэша, что является наиболее кратким и эффективным способом проверки равенства двух словарей?
В этом контексте два словаря называются равными, если они содержат один и тот же набор ключей (порядок не важен), и для каждого такого ключа они соглашаются на значение.
вот несколько способов, с которыми я пришел (возможно, еще много):
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()));
}
dic1.Count == dic2.Count && !dic1.Except(dic2).Any();
Это действительно зависит от того, что вы подразумеваете под равенством.
Этот метод будет проверять, что два словаря содержат одни и те же ключи с одинаковыми значениями (при условии, что оба словаря используют ту же реализацию 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;
}
Вы можете использовать linq для сравнения ключей/значений:
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]));
}
bool equals = a.Intersect(b).Count() == a.Union(b).Count()
относится к массивам, но по мере использования методов IEnumerable<T>
он также может использоваться для Dictionary<K,V>
.
Я думал, что принятый ответ будет правильным, основываясь на том, что я читал в smarthelp для метода Except: "Производит заданное различие двух последовательностей, используя сопоставитель равенства по умолчанию для сравнения значений". Но я обнаружил, что это не очень хороший ответ.
Рассмотрим этот код:
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))))));
В результате получается следующее:
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<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;
}
Также посмотрите, как изменится результат, если: Ключевой порядок не тот. (возвращает false)
newDict = new Dictionary<string, List<string>>()
{{"001A", new List<string> {"John", "Doe"}},
{"003C", new List<string> {"Doe", "Jane"}},
{"002B", new List<string> {"Frank", "Abignale"}}};
и Сопоставления клавиш, но значение не соответствует (возвращает 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"}}};
Если порядок последовательности не имеет значения, функция может быть изменена на следующее, но, скорее всего, произойдет сбой производительности.
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;
}
Проверьте, не использует ли DictionaryEqual_NoSort следующее для newDict (DictionaryEquals_NoSort возвращает 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"}}};
Если два словаря содержат одни и те же ключи, но в другом порядке, должны ли они считаться равными? Если нет, то словари должны сравниваться путем запуска счетчиков через оба одновременно. Это, вероятно, будет быстрее, чем перечисление через один словарь и поиск каждого элемента в другом. Если у вас есть предварительное знание о том, что равные словари будут иметь свои элементы в одном порядке, такая двойная перепись, вероятно, является способом выхода.
В вопросах OP он сказал, что тест равенства должен охватывать не только сопоставление ключей, но и их значение ". В этом контексте два словаря называются равными, если они содержат один и тот же набор ключей (порядок не важен), и для каждого такого ключа, они согласны с значением.
Я что-то упускаю или отмеченный ответ fooobar.com/questions/135072/... проверяет только на равенство размера и ключей, но не на их значение?
Я бы разместил это рядом с ответом, но не смог понять, как добавить его в качестве комментария, извините.
Для вложенных словарей и списков я собрал пару идей, чтобы сделать это: https://gist.github.com/NullVoxPopuli/f95baaa48b4e9854dcfe (слишком много кода для публикации здесь ) ~ 100 строк