Como criar dicionários case insensitive

Dicionários são sempre listados entre as melhores estruturas de dados pois o custo de acesso a um dado registro é O(1), ou seja, não é necessário percorrer todo dataset para encontrar um elemento, basta acessar o seu índice diretamente. Mas como criar dicionários case insensitive? Ou em outras palavras, como criar dicionários que ignorem a forma como uma palavra foi escrita?

O código abaixo armazena o estoque de produtos para uma determinada loja. Sempre que um produto é adicionado ele verifica se o produto já existe, caso exista aumenta a quantidade em estoque, quando não existe cria um novo registro.

namespace Dictionary {
   public class Store {
      public Dictionary<string, int> Items = new();

      public void Add(string product, int quant) {
         if (Items.ContainsKey(product)) {
            Items[product] += quant;
         }
         else {
            Items.Add(product, quant);
         }
      }
   }
   public static class DictionaryExample {
      public static void Main() {
         var store = new Store();
         store.Add("Bike", 10);
         store.Add("bike", 15);
         store.Add("Skate", 8);

         foreach (var item in store.Items) {
            Console.WriteLine($"{item.Key} - {item.Value}");
         }
         Console.ReadKey();
      }
   }
}

O problema é que, por padrão, o runtime trata strings como case sensitive, então “Bike”, “BIKE”, …. e “bike” são palavras diferentes, resultando em dois diferentes produtos:

Bike - 10
bike - 15
Skate - 8

Uma solução simples

Uma solução simples para o problema é converter todas as letras do nome do produto para minúsculas ou maiúsculas, usando ToLower ou ToUpper no método Add():

public void Add(string product, int quant) {
   product = product.ToLower();
   if (Items.ContainsKey(product)) {
      Items[product] += quant;
   }
   else {
      Items.Add(product, quant);
   }
}

O que trará como resultado:

bike - 25
skate - 8

Funciona, mas não é o ideal.

Uma solução eficaz

Uma solução melhor para o problema é especificar ao runtime como ele deve tratar as strings armazenadas no dicionário usando a classe StringComparer no momento que o dicionário é instanciado.

Então, ao invés de instanciar o objeto simplesmente usando new(), basta passar o tipo de comparação desejada no construtor no mesmo. A opção InvariantCultureIgnoreCase fará com que as strings “Bike” e “bike” sejam consideradas a mesma sem a necessidade do uso do método ToLower e produzirá exatamente a mesma saída.

public Dictionary<string, int> Items = new(StringComparer.InvariantCultureIgnoreCase);

Quer saber mais sobre os diferentes tipos de comparação, então acesse a documentação oficial da Microsoft: StringComparer Class