Acceso que no distingue entre mayúsculas y minúsculas para el diccionario genérico

Resuelto TocToc asked hace 12 años • 4 respuestas

Tengo una aplicación que utiliza DLL administrados. Uno de esos dlls devuelve un diccionario genérico:

Dictionary<string, int> MyDictionary;  

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

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

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

Esperaba que TryGetValue tuviera un indicador de ignorar mayúsculas y minúsculas como se menciona en el documento de MSDN , 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? ¿Existe una solución mejor que crear una nueva copia del diccionario con el parámetro StringComparer.OrdinalIgnoreCase adecuado?

TocToc avatar Nov 05 '12 17:11 TocToc
Aceptado

No hay forma de especificar a StringCompareren el punto en el que intenta obtener un valor. Si lo piensa bien, "foo".GetHashCode()son "FOO".GetHashCode()totalmente diferentes, por lo que no hay una forma razonable de implementar un get que no distinga entre mayúsculas y minúsculas en un mapa hash que distinga entre mayúsculas y minúsculas.

Sin embargo, puede crear un diccionario que no distinga entre mayúsculas y minúsculas usando:-

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

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

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

Este nuevo diccionario luego usa la GetHashCode()implementación StringComparer.OrdinalIgnoreCasey le da el comparer.GetHashCode("foo")mismo comparer.GetHashcode("FOO")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 diccionario IEnumerable<KeyValuePair<TKey, TValue>>y simplemente iterarlo:-

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

O si lo prefieres, sin 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 a cambio el costo de una búsqueda es O(n) en lugar de O(1).

Iain Galloway avatar Nov 05 '2012 10:11 Iain Galloway

Para los usuarios de LINQ que nunca utilizan un constructor de diccionario normal

myCollection.ToDictionary(x => x.PartNumber, x => x.PartDescription, StringComparer.OrdinalIgnoreCase)
Derpy avatar Mar 09 '2017 19:03 Derpy

Hay una forma mucho más sencilla:

using System;
using System.Collections.Generic;
....
var caseInsensitiveDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
Major avatar Sep 28 '2020 13:09 Major

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

var item = MyDictionary.Where(x => x.Key.ToLower() == MyIndex.ToLower()).FirstOrDefault();
    if (item != null)
    {
        TheValue = item.Value;
    }
Shoham avatar Dec 03 '2014 08:12 Shoham