2010-09-27 18 views
40

Suponiendo que las claves y valores del diccionario tienen sus métodos equals y hash implementados correctamente, ¿cuál es la forma más sucinta y eficiente de probar la igualdad de dos diccionarios?Prueba de igualdad entre diccionarios en C#

En este contexto, se dice que dos diccionarios son iguales si contienen el mismo conjunto de claves (orden no importante), y para cada una de esas claves, acuerdan el valor.

Aquí hay algunas maneras que se me ocurrió (hay probablemente muchos más):

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())); 
} 

Respuesta

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

Esto no me parece correcto. –

+2

Lo sentimos, ahora hemos agregado la falta para no corregirla. Los diccionarios son equivalentes si son del mismo tamaño y no hay elementos que estén en el primero y no en el segundo. –

+0

Eso parece mejor;) –

11

Realmente depende de lo que entendemos por igualdad.

Este método probará que dos diccionarios contienen las mismas claves con los mismos valores (suponiendo que ambos diccionarios usan la misma implementación 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; 
} 
+0

+1 Niza, me gusta – w69rdy

+2

no estás vaciando el diccionario? comparex fallaría la segunda vez que se llama dado que el segundo parámetro está vacío. ¿Por qué modificar un diccionario? ¿No viola un principio sobre una simple verificación de igualdad? –

+0

@SB: Muy buen punto. El método 'CompareX' no debería realmente mutar ninguno de los diccionarios que se le pasan. Se suponía que era una simple prueba de concepto, pero editaré para que sea más seguro. – LukeH

3

Usted podría utilizar LINQ para las comparaciones clave/valor:

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])); 
} 
+0

'TValue val;' 'retorno dict1.Count == dict2.Count && dict1.All (x => dict2.TryGetValue (x.Key, fuera val) && valueComparer.Equals (x.Value, val)); '? –

1

@Allen's answer:

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

es acerca de las matrices pero por lo que IEnumerable<T> métodos se utilizan, se se puede usar también para Dictionary<K,V>.

0

Si dos diccionarios contienen las mismas claves, pero en diferente orden, ¿deberían considerarse iguales? De lo contrario, los diccionarios deberían compararse ejecutando enumeradores a través de ambos simultáneamente. Esto probablemente será más rápido que enumerar a través de un diccionario y buscar cada elemento en el otro. Si tiene conocimiento previo de que los diccionarios con igual tendrán sus elementos en el mismo orden, tal doble enumeración es probablemente el camino a seguir.

+0

Depende de su aplicación, supongo. En mi caso particular, el orden clave no importa y el orden de los valores, cuando se compara con Key similar, no importa. – Machtyn

+0

Si necesita una comparación independiente de la orden, un tipo de diccionario personalizado que incluye soporte técnico para tal cosa probablemente sea más rápido que cualquiera de los tipos incorporados. De lo contrario, si controlas cuándo se agregan o eliminan elementos de los diccionarios, puede ser útil calcular el código hash de cada elemento que se agrega o elimina y mantener un 'UInt64' en ejecución total de' (hash + 0x123456789L) * hash', haciendo el cálculo en un contexto 'no verificado' [cuando se agregan elementos, agregue el valor anterior al total; cuando se elimine, restarlo]. Si dos colecciones tienen totales desiguales ... – supercat

+0

... no es necesario comparar sus contenidos. Del mismo modo si tienen tamaños desiguales. Si los tamaños son iguales, y los hash extendidos sumados son iguales, y uno puede presumir que las colecciones usan el mismo 'EqualityComparer', recorra uno y verifique si el otro contiene todos los elementos. – supercat

0

En las preguntas de OP dice que la prueba de igualdad debe cubrir no solo la coincidencia de claves sino también su valor "En este contexto, se dice que dos diccionarios son iguales si contienen el mismo conjunto de claves (no importante) y para cada clave, están de acuerdo con el valor ".

¿Me falta algo o la respuesta marcada https://stackoverflow.com/a/3804852/916121 solo verifica la igualdad de tamaño y las claves, pero no su valor?

Lo habría publicado al lado de la respuesta, pero no he podido encontrar la manera de agregarlo como comentario, lo siento.

+0

Sí, te estás perdiendo algo. La respuesta compara las entradas (pares clave-valor) no las claves. –

1

pensé que la respuesta aceptada sería correcto en función de lo que Estaba leyendo en la ayuda inteligente para el método Excepto: "Produce la diferencia establecida de dos secuencias usando el comparador de igualdad predeterminado para comparar valores". Pero descubrí que no es una buena respuesta.

consideran este código:

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)))))); 

Esto se traduce en lo siguiente:

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

Como se puede ver, tanto "oldDict" y "newDict" se configuran exactamente lo mismo. Y ni la solución sugerida ni una llamada a SequenceEqual funcionan correctamente. Me pregunto si es el resultado del Excepto el uso de la carga diferida o la forma en que el comparador está configurado para el Diccionario. (Aunque, mirando la estructura y las explicaciones de referencia sugieren que debería).

Aquí está la solución que se me ocurrió. Tenga en cuenta que la regla que utilicé es la siguiente: dos diccionarios son iguales si ambos contienen las mismas claves y los valores para cada coincidencia de clave. Tanto las claves como los valores deben estar en el mismo orden secuencial. Y mi solución puede no ser la más eficiente, ya que depende de iterar a través de todo el conjunto de claves.

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; 
} 

también ver cómo cambian los resultados si: orden de la clave no es lo mismo. (Devuelve 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"}}}; 

y partidos orden de la clave, pero el valor no coincide (devuelve 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"}}}; 

Si el orden de secuencia no importa, la función se puede cambiar a la siguiente, pero es probable que haya un golpe de rendimiento.

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; 
} 

Comprobar si el DictionaryEqual_NoSort utilizando la siguiente para newDict (DictionaryEquals_NoSort devuelve 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"}}};  
+0

En mi método DictionaryEquals, no estaba seguro de si necesito o no el cheque de Count. ¿SequenceEqual ya lo hace? – Machtyn

+0

Además, si mi configuración de la respuesta aceptada y la prueba de que falla es incorrecta, no dude en corregirme. – Machtyn

+0

La respuesta aceptada SÍ funciona para tipos incorporados. – Machtyn