Как сравнить два словаря в С#
У меня есть два общих словаря. Оба имеют одинаковые ключи. Но значения могут быть разными. Я хочу сравнить 2-й словарь с 1-м словарем. Если есть различия между значениями, я хочу сохранить эти значения в отдельном словаре.
1st Dictionary
------------
key Value
Barcode 1234566666
Price 20.00
2nd Dictionary
--------------
key Value
Barcode 1234566666
Price 40.00
3rd Dictionary
--------------
key Value
Price 40
Кто-нибудь может дать мне лучший алгоритм для этого. Я написал алгоритм, но он имеет много циклов. Я ищу короткую и эффективную идею. Также как решение с использованием выражения запроса LINQ или лямда-выражения LINQ. Я использую .Net Framework 3.5 с С#. Я нашел кое-что о методе Except(). Но, к сожалению, я не мог понять, что происходит с этим методом. Хорошо, если кто-нибудь объяснит предложенный алгоритм.
Ответы
Ответ 1
Если вы уже отметили, что ключи одинаковы, вы можете просто использовать:
var dict3 = dict2.Where(entry => dict1[entry.Key] != entry.Value)
.ToDictionary(entry => entry.Key, entry => entry.Value);
Чтобы объяснить, это будет:
- Итерации по парам ключ/значение в
dict2
- Для каждой записи найдите значение в
dict1
и отфильтруйте любые записи, в которых эти два значения совпадают.
- Создайте словарь из оставшихся записей (т.е. те, где значение
dict1
отличается), взяв ключ и значение из каждой пары так же, как они появляются в dict2
.
Обратите внимание, что это избегает полагаться на равенство KeyValuePair<TKey, TValue>
- на это может положиться положиться, но лично я нахожу это более ясным. (Он также будет работать, когда вы используете собственный сопоставитель сравнений для клавиш словаря - хотя вам также нужно передать это значение в ToDictionary
).
Ответ 2
попробуйте:
dictionary1.OrderBy(kvp => kvp.Key)
.SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))
Ответ 3
Вы упомянули, что оба словаря имеют одинаковые ключи, поэтому, если это предположение верно, вам не нужно ничего фантастического:
foreach (var key in d1.Keys)
{
if (!d1[key].Equals(d2[key]))
{
d3.Add(key, d2[key]);
}
}
Или я не понимаю вашу проблему?
Ответ 4
чтобы проверить разницу,
dic1.Count == dic2.Count && !dic1.Except(dic2).Any();
следующий код возвращает все разные значения
dic1.Except(dic2)
Ответ 5
Вы можете присоединиться к ним на своих клавишах и выбрать оба значения. Затем вы можете фильтровать на основе того, являются ли значения одинаковыми или разными. Наконец, вы можете преобразовать коллекцию в словарь с ключами и вторыми значениями.
var compared = first.Join( second, f => f.Key, s => s.Key, (f,s) => new { f.Key, FirstValue = f.Value, SecondValue = s.Value } )
.Where( j => j.FirstValue != j.SecondValue )
.ToDictionary( j => j.Key, j => j.SecondValue );
Использование цикла также не должно быть слишком плохим. Я подозреваю, что они будут иметь схожие характеристики.
var compared = new Dictionary<string,object>();
foreach (var kv in first)
{
object secondValue;
if (second.TryGetValue( kv.Key, out secondValue ))
{
if (!object.Equals( kv.Value, secondValue ))
{
compared.Add( kv.Key, secondValue );
}
}
}
Ответ 6
Предполагая, что оба словаря имеют одинаковые ключи, самый простой способ -
var result = a.Except(b).ToDictionary(x => x.Key, x => x.Value);
ИЗМЕНИТЬ
Обратите внимание, что a.Except(b)
дает другой результат от b.Except(a)
:
a.Except(b): Price 20
b.Except(a): Price 40
Ответ 7
var diff1 = d1.Except(d2);
var diff2 = d2.Except(d1);
return diff1.Concat(diff2);
Изменить: Если вы уверены, что все ключи одинаковы, вы можете:
var diff = d2.Where(x=>x.Value != d1[x.Key]).ToDictionary(x=>x.Key, x=>x.Value);
Ответ 8
преобразование объекта в словарь, а затем после того, как концепция набора вычитает их, элементы результата должны быть пустыми, если они идентичны.
public static IDictionary<string, object> ToDictionary(this object source)
{
var fields = source.GetType().GetFields(
BindingFlags.GetField |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source) ?? string.Empty
);
var properties = source.GetType().GetProperties(
BindingFlags.GetField |
BindingFlags.GetProperty |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null) ?? string.Empty
);
return fields.Concat(properties).ToDictionary(key => key.Key, value => value.Value); ;
}
public static bool EqualsByValue(this object source, object destination)
{
var firstDic = source.ToFlattenDictionary();
var secondDic = destination.ToFlattenDictionary();
if (firstDic.Count != secondDic.Count)
return false;
if (firstDic.Keys.Except(secondDic.Keys).Any())
return false;
if (secondDic.Keys.Except(firstDic.Keys).Any())
return false;
return firstDic.All(pair =>
pair.Value.ToString().Equals(secondDic[pair.Key].ToString())
);
}
public static bool IsAnonymousType(this object instance)
{
if (instance == null)
return false;
return instance.GetType().Namespace == null;
}
public static IDictionary<string, object> ToFlattenDictionary(this object source, string parentPropertyKey = null, IDictionary<string, object> parentPropertyValue = null)
{
var propsDic = parentPropertyValue ?? new Dictionary<string, object>();
foreach (var item in source.ToDictionary())
{
var key = string.IsNullOrEmpty(parentPropertyKey) ? item.Key : $"{parentPropertyKey}.{item.Key}";
if (item.Value.IsAnonymousType())
return item.Value.ToFlattenDictionary(key, propsDic);
else
propsDic.Add(key, item.Value);
}
return propsDic;
}
originalObj.EqualsByValue(messageBody); // will compare values.
источник кода