Cómo comparar dos diccionarios en C#
algorithm data-structures (8)
Tengo dos Diccionarios genéricos. Ambos tienen las mismas claves. Pero los valores pueden ser diferentes. Quiero comparar el 2º diccionario con el 1er diccionario. Si hay diferencias entre los valores, quiero almacenar esos valores en un diccionario separado.
1st Dictionary
------------
key Value
Barcode 1234566666
Price 20.00
2nd Dictionary
--------------
key Value
Barcode 1234566666
Price 40.00
3rd Dictionary
--------------
key Value
Price 40
Cualquiera puede darme un mejor algoritmo para hacer esto. Escribí un algoritmo, pero tiene muchos bucles. Estoy buscando una idea corta y eficiente. También me gusta una solución utilizando la expresión de consulta LINQ o la expresión lamda LINQ. Lo estoy usando. Net Framework 3.5 con C #. Encontré algo sobre el método Except (). Pero desafortunadamente no pude entender lo que está sucediendo con ese método. Es fantástico si alguien explica el algoritmo sugerido. Siempre me gusta aprender :).
Gracias Thabo.
convirtiendo el objeto al diccionario y luego siguiendo el concepto del conjunto, restájalos, los elementos de los resultados deben estar vacíos en caso de que sean idénticos.
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.
Debería poder unirlos en sus claves y seleccionar ambos valores. Luego puede filtrar en función de si los valores son iguales o diferentes. Finalmente, puede convertir la colección en un diccionario con las claves y segundos valores.
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 );
Usar un bucle tampoco debería ser tan malo. Sospecho que tendrían características de rendimiento similares.
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 );
}
}
}
Mencionó que ambos diccionarios tienen las mismas claves, por lo que si esta suposición es correcta, no necesita nada sofisticado:
foreach (var key in d1.Keys)
{
if (!d1[key].Equals(d2[key]))
{
d3.Add(key, d2[key]);
}
}
¿O estoy malinterpretando tu problema?
Si ya has verificado que las claves son las mismas, puedes usar:
var dict3 = dict2.Where(entry => dict1[entry.Key] != entry.Value)
.ToDictionary(entry => entry.Key, entry => entry.Value);
Para explicar, esto va a:
- Iterar sobre los pares clave / valor en
dict2
- Para cada entrada, busque el valor en
dict1
y filtre las entradas donde los dos valores son los mismos - Forme un diccionario a partir de las entradas restantes (es decir, aquellas en las que el valor
dict1
es diferente) tomando la clave y el valor de cada par tal como aparecen endict2
.
Tenga en cuenta que esto evita depender de la igualdad de KeyValuePair<TKey, TValue>
; podría estar bien confiar en eso, pero personalmente lo encuentro más claro. (También funcionará cuando uses un comparador de igualdad personalizado para las claves del diccionario, aunque también deberías pasarlo a ToDictionary
).
Suponiendo que ambos diccionarios tengan las mismas claves, la forma más sencilla es
var result = a.Except(b).ToDictionary(x => x.Key, x => x.Value);
EDITAR
Tenga en cuenta que a.Except(b)
da un resultado diferente de b.Except(a)
:
a.Except(b): Price 20
b.Except(a): Price 40
para comprobar cualquier diferencia,
dic1.Count == dic2.Count && !dic1.Except(dic2).Any();
Código siguiente devuelve todos los valores diferentes
dic1.Except(dic2)
tratar :
dictionary1.OrderBy(kvp => kvp.Key)
.SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))
var diff1 = d1.Except(d2);
var diff2 = d2.Except(d1);
return diff1.Concat(diff2);
Editar: Si estás seguro de que todas las teclas son iguales puedes hacer:
var diff = d2.Where(x=>x.Value != d1[x.Key]).ToDictionary(x=>x.Key, x=>x.Value);