¿Comparando dos diccionarios para datos iguales en c #?

Tengo dos diccionarios que contienen una clave de cadena y luego un objeto. El objeto contiene cinco campos. ¿Existe una manera elegante de garantizar que los dos diccionarios contengan las mismas claves y luego, si esto es correcto, contengan los mismos cinco campos por objeto?

¿Los dos diccionarios tienen el mismo código hash incorporado o algo así?

EDIT, no parece estar trabajando para el siguiente código:

Dictionary test1 = new Dictionary(); Dictionary test2 = new Dictionary(); MyClass i = new MyClass("", "", 1, 1, 1, 1); MyClass j = new MyClass("", "", 1, 1, 1, 1); test1.Add("1", i); test2.Add("1", j); bool equal = test1.OrderBy(r => r.Key).SequenceEqual(test2.OrderBy(r => r.Key)); class MyClass { private string a; private string b; private long? c; private decimal d; private decimal e; private decimal f; public MyClass(string aa, string bb, long? cc, decimal dd, decimal ee, decimal ff) { a= aa; b= bb; c= cc; d= dd; e= ee; f= ff; } 

esto devuelve falso?

Primero debe anular el método Equals y GetHashCode en su clase, de lo contrario, la comparación se realizará en referencias en lugar de valores reales. (El código para anular Equals y GetHashCode se proporciona al final) , después puede usar:

 var result = (dic1 == dic2) || //Reference comparison (if both points to same object) (dic1.Count == dic2.Count && !dic1.Except(dic2).Any()); 

Dado que el orden en que se devuelven los artículos en el Diccionario no está definido , no puede confiar en Dictionary.SequenceEqual (sin OrderBy ) .

Puedes probar:

 Dictionary dic1 = new Dictionary(); Dictionary dic2 = new Dictionary(); dic1.Add("Key1", new { Name = "abc", Number = "123", Address = "def", Loc = "xyz" }); dic1.Add("Key2", new { Name = "DEF", Number = "123", Address = "def", Loc = "xyz" }); dic1.Add("Key3", new { Name = "GHI", Number = "123", Address = "def", Loc = "xyz" }); dic1.Add("Key4", new { Name = "JKL", Number = "123", Address = "def", Loc = "xyz" }); dic2.Add("Key1",new { Name = "abc",Number= "123", Address= "def", Loc="xyz"}); dic2.Add("Key2", new { Name = "DEF", Number = "123", Address = "def", Loc = "xyz" }); dic2.Add("Key3", new { Name = "GHI", Number = "123", Address = "def", Loc = "xyz" }); dic2.Add("Key4", new { Name = "JKL", Number = "123", Address = "def", Loc = "xyz" }); bool result = dic1.SequenceEqual(dic2); //Do not use that 

La mayoría de las veces lo anterior se volverá true , pero no se puede confiar en eso debido a la naturaleza no ordenada del Dictionary .

Como SequenceEqual también comparará el orden, por lo tanto, confiar solo en SequenceEqual podría estar equivocado . Tienes que usar OrderBy para ordenar ambos diccionarios y luego usar SequenceEqual como:

 bool result2 = dic1.OrderBy(r=>r.Key).SequenceEqual(dic2.OrderBy(r=>r.Key)); 

Pero eso implicará múltiples iteraciones, una para ordenar y la otra para comparar cada elemento usando SequenceEqual .

Código para anular Equals y GetHashCode

 private class MyClass { private string a; private string b; private long? c; private decimal d; private decimal e; private decimal f; public MyClass(string aa, string bb, long? cc, decimal dd, decimal ee, decimal ff) { a = aa; b = bb; c = cc; d = dd; e = ee; f = ff; } protected bool Equals(MyClass other) { return string.Equals(a, other.a) && string.Equals(b, other.b) && c == other.c && e == other.e && d == other.d && f == other.f; } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((MyClass)obj); } public override int GetHashCode() { unchecked { var hashCode = (a != null ? a.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (b != null ? b.GetHashCode() : 0); hashCode = (hashCode * 397) ^ c.GetHashCode(); hashCode = (hashCode * 397) ^ e.GetHashCode(); hashCode = (hashCode * 397) ^ d.GetHashCode(); hashCode = (hashCode * 397) ^ f.GetHashCode(); return hashCode; } } } 

También puede ver: Forma correcta de anular Equals () y GetHashCode ()

Puedes usar

 bool dictionariesEqual = dic1.Keys.Count == dic2.Keys.Count && dic1.Keys.All(k => dic2.ContainsKey(k) && object.Equals(dic2[k], dic1[k])); 

La función Equals incorporada del Dictionary solo verifica la igualdad de referencia, vea esta pregunta en SO . Los códigos hash no le dicen de manera confiable si dos objetos son iguales; Siempre hay una posibilidad de colisión de hachís. ¡Nunca uses hashcodes como prueba de igualdad!

Lo haría a mano: Compare el recuento de entradas de ambos diccionarios, repita la iteración sobre los pares clave-valor de un diccionario y compruebe si la clave existe en el otro y compare los objetos correspondientes de ambos diccionarios. Edición: Ver respuesta de Rawling 🙂

Hubo un par de respuestas aquí que creo que se acercaron bastante, pero hubo un par de puntos adicionales que pensé que deberían agregarse, así que las estoy agregando como otra posible respuesta.

Primero, evitaría usar el método SequenceEquals. Es un método de extensión para Enumerable e implícitamente requiere que las dos colecciones estén en el mismo orden. Los diccionarios no están destinados a ser colecciones ordenadas, por lo que usar SequenceEquals significa que tendrá que iterar innecesariamente sobre los dos diccionarios para crear colecciones intermedias ordenadas / ordenadas que tampoco necesita y luego iterar sobre esas colecciones para compararlas por igualdad. . Eso parece realmente ineficiente y un abuso de LINQ, todo en el nombre de intentar ser conciso y escribir una solución de una línea. Si la idea de “elegante” del OP es escasa, supongo que esto hará el truco, pero parece un desperdicio.

Por otro lado, si la idea de OP de “elegante” es eficiente, entonces probablemente deba escribir un poco más de código. Primero, debe anular el método Equals para su clase o implementar IEquatable en su clase (vea aquí , por ejemplo). Esto le permitirá comparar los valores en el diccionario. Entonces, probablemente querrá hacer algo como implementar una interfaz como IEqualityComparer para su diccionario.

Entonces, la comparación de los dos diccionarios sería algo como a continuación. Es solo un ejemplo rápido y sucio de “la parte de atrás de la servilleta”, por lo que no es un ejemplo de la mejor manera de hacerlo, sino que pretende ilustrar una forma de iterar solo tantas veces sobre el diccionario como sea necesario y salir tan pronto como sea posible. Se encuentra una desigualdad.

Primero el código requerido:

 public class Foo { //members here... public override bool Equals(object obj) { //implementation here } //You should probably also override GetHashCode to be thorough, //but that's an implementation detail... } //This method could stand on its own or you could change it to make it //part of the implementation of one of the comparison interfaces... bool DictionariesEqual(Dictionary x, Dictionary y) { //If we're comparing the same object, it's obviously equal to itself. if(x == y) { return true; } //Make sure that we don't have null objects because those are //definitely not equal. if (x == null || y == null) { return false; } //Stop processing if at any point the dictionaries aren't equal. bool result = false; //Make sure that the dictionaries have the same count. result = x.Count == y.Count; //If we passed that check, keep going. if(result) { foreach(KeyValuePair xKvp in x) { //If we don't have a key from one in the other, even though //the counts are the same, the dictionaries aren't equal so //we can fail out. Foo yValue; if(!y.TryGetValue(xKvp.Key, out yValue)) { result = false; break; } else { //Use the override of the Equals method for your object //to see if the value from y is equal to the value from //x. result = xKvp.Value.Equals(yValue); if(!result) { //If they're not equal we can just quit out. break; } } } } return result; } 

Entonces lo usaríamos así:

 Dictionary dict1 = new Dictionary(); Dictionary dict2 = new Dictionary(); //Fill the dictionaries here... //Compare the dictionaries bool areDictsEqual = DictionariesEqual(dict1, dict2); 

Entonces, no es el código más conciso, pero tampoco itera más de lo necesario. En mi opinión, eso es más elegante.

En este caso, solo puede usar el método SequenceEquals () – como el siguiente:

  Dictionary d1 = new Dictionary(); d1.Add("first", new { Name = "TestName", Age = 12, ID = 001 }); Dictionary d2 = new Dictionary(); d2.Add("first", new { Name = "TestName", Age = 12, ID = 001 }); Console.WriteLine(d1.SequenceEqual(d2)); //outputs True 

Nota: para simplificar, utilicé clases implícitas para llenar los diccionarios. El código funcionará de la misma manera con cualquier objeto. Los hashcodes de ambos diccionarios no son iguales, lo que puede verificarse fácilmente haciendo lo siguiente:

  Console.WriteLine(d1.GetHashCode() + " " + d2.GetHashCode()); //outputs different hashcodes