Acceso insensible a mayúsculas y minúsculas para el diccionario genérico


Tengo una aplicación que usa DLL administrados. Una de esas DLL devuelve un diccionario genérico:

Dictionary<string, int> MyDictionary;  

El diccionario contiene claves con mayúsculas y minúsculas.

Por otro lado, estoy recibiendo una lista de claves potenciales (cadena), sin embargo, no puedo garantizar el caso. Estoy tratando de obtener el valor en el diccionario usando las claves. Pero, por supuesto, lo siguiente fallará ya que tengo un caso desajuste:

bool Success = MyDictionary.TryGetValue( MyIndex, out TheValue );  

Esperaba que el TryGetValue tuviera un ignorar case bandera como se menciona en el MSDN doc, pero parece que esto no es válido para diccionarios genéricos.

¿Hay alguna manera de obtener el valor de ese diccionario ignorando el caso clave? ¿Hay una mejor solución que crear una nueva copia del diccionario con el StringComparer apropiado?¿Parámetro OrdinalIgnoreCase ?

Author: TocToc, 2012-11-05

3 answers

No hay forma de especificar un StringComparer en el punto donde se intenta obtener un valor. Si lo piensas, "foo".GetHashCode() y "FOO".GetHashCode() son totalmente diferentes, por lo que no hay una manera razonable de implementar un get insensible a mayúsculas y minúsculas en un mapa hash sensible a mayúsculas y minúsculas.

Sin embargo, puede crear un diccionario que no distingue entre mayúsculas y minúsculas en primer lugar usando: -

var comparer = StringComparer.OrdinalIgnoreCase;
var caseInsensitiveDictionary = new Dictionary<string, int>(comparer);

O cree un nuevo diccionario que no distingue entre mayúsculas y minúsculas con el contenido de un diccionario existente que distingue entre mayúsculas y minúsculas (si está seguro de que no hay colisiones caso): -

var oldDictionary = ...;
var comparer = StringComparer.OrdinalIgnoreCase;
var newDictionary = new Dictionary<string, int>(oldDictionary, comparer);

Este diccionario usa entonces la implementación GetHashCode() en StringComparer.OrdinalIgnoreCase tal que comparer.GetHashCode("foo") y comparer.GetHashcode("FOO") le dan el mismo valor.

Alternativamente, si solo hay unos pocos elementos en el diccionario, y/o solo necesita buscar una o dos veces, puede tratar el diccionario original como un IEnumerable<KeyValuePair<TKey, TValue>> y simplemente iterar sobre él: -

var myKey = ...;
var myDictionary = ...;
var comparer = StringComparer.OrdinalIgnoreCase;
var value = myDictionary.FirstOrDefault(x => String.Equals(x.Key, myKey, comparer))?.Value;

O si lo prefiere, sin el LINQ: -

var myKey = ...;
var myDictionary = ...;
var comparer = StringComparer.OrdinalIgnoreCase;
int? value;
foreach (var element in myDictionary)
{
  if (String.Equals(element.Key, myKey, comparer))
  {
    value = element.Value;
    break;
  }
}

Esto le ahorra el costo de crear una nueva estructura de datos, pero en devolver el costo de una búsqueda es O(n) en lugar de O (1).

 361
Author: Iain Galloway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-10 13:04:46

Para ustedes LINQers por ahí que nunca utilizan un constructor de diccionario regular:

myCollection.ToDictionary(x => x.PartNumber, x => x.PartDescription, StringComparer.OrdinalIgnoreCase)
 15
Author: Derpy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-06 11:31:46

No es muy elegante, pero en caso de que no pueda cambiar la creación del diccionario, y todo lo que necesita es un truco sucio, ¿qué tal esto:

var item = MyDictionary.Where(x => x.Key.ToLower() == MyIndex.ToLower()).FirstOrDefault();
    if (item != null)
    {
        TheValue = item.Value;
    }
 11
Author: Shoham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-03 08:58:09