Dicionário Case-INsensitive com tipo de chave string em C #

156

Se eu tiver um, Dictionary<String,...>é possível criar métodos como ContainsKeysem distinção entre maiúsculas e minúsculas?

Isso parecia relacionado, mas não o entendi direito: Dicionário c #: tornando a Chave sem distinção entre maiúsculas e minúsculas através de declarações

Mr. Boy
fonte
6
O que há de errado em usar StringComparer.InvariantCultureIgnoreCase? Ele faz o que diz ...
leppie
5
Eu nunca tinha ouvido falar, daí a pergunta!
Mr. Boy
2
Possível duplicado do Caso acesso insensível para dicionário genérico
Michael Freidgeim
Essa questão está relacionada, mas não é uma duplicata desta. Essa fala sobre como lidar com um dicionário existente. Estou começando com um novo código, então as respostas aqui são mais adequadas.
goodeye 27/02/19

Respostas:

322

Isso parecia relacionado, mas não o entendi direito: Dicionário c #: tornando a Chave sem distinção entre maiúsculas e minúsculas através de declarações

Está de fato relacionado. A solução é instruir a instância do dicionário a não usar o método de comparação de cadeias padrão (que diferencia maiúsculas de minúsculas), mas a usar uma que não diferencia maiúsculas de minúsculas. Isso é feito usando o construtor apropriado :

var dict = new Dictionary<string, YourClass>(
        StringComparer.InvariantCultureIgnoreCase);

O construtor espera um IEqualityComparerque diga ao dicionário como comparar chaves.

StringComparer.InvariantCultureIgnoreCasefornece uma IEqualityComparerinstância que compara cadeias de caracteres que não diferenciam maiúsculas de minúsculas.

Konrad Rudolph
fonte
1
Perfeito, de alguma forma eu tinha perdido a maneira do STL de passar o comparador para o ctor.
Mr. Boy
7
Nunca deixa de me surpreender como sempre encontro a resposta para qualquer pergunta sobre codificação aqui! Talvez eu nomeie meu próximo gato depois de você, Konrad! :-)
Jamie
10
Possível, o comparador StringComparison.OrdinalIgnoreCase mais rapidamente que com base na cultura: stackoverflow.com/questions/492799/…
Manushin Igor 30/03/15
Como faço isso se não estou criando um dicionário, mas tentando usar ContainsKey () para uma correspondência de chave que não diferencia maiúsculas de minúsculas em um dicionário existente que recebo como parte de um objeto?
Shridhar R Kulkarni
1
@ShridharRKulkarni Você fundamentalmente não pode (eficientemente). A lógica de comparação é uma parte essencial da estrutura de dados do dicionário interno. Para permitir isso, o contêiner precisaria manter vários índices para seus dados, e os dicionários não fazem isso.
Konrad Rudolph
21
var myDic = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
myDic.Add("HeLlo", "hi");

if (myDic.ContainsKey("hello"))
    Console.WriteLine(myDic["hello"]);
Steve
fonte
1
O snippet de código diz tudo. Muito útil. Não sei por que essa postagem tem menos votos positivos em comparação com a resposta aceita apenas por estar atrasada em três minutos.
RBT
14

Existem poucas chances de o seu negócio com o dicionário ser retirado de terceiros ou de dll externa. Usando linq

YourDictionary.Any(i => i.KeyName.ToLower().Contains("yourstring")))

Kurkula
fonte
3
Isso funciona muito bem. Uma palavra de cautela, no entanto, se você alterar Anypara SingleOrDefaultvocê não nullvoltará se não existir, em vez disso, receberá um par de valor-chave com a chave e o valor definidos como null!
precisa saber é o seguinte
1
ContainsParece um caso de uso muito específico para algo em que você estava trabalhando no momento. Como uma resposta útil mais genérica, acho que Equalsé melhor. E na mesma nota, em vez de duplicar uma string com ToLower(), seria ainda melhor usar StringComparison.xxxCase.
Suamere 6/02/2018
1
isso foi muito útil e livrar-se do ToLower é definitivamente uma melhoria - meu caso de uso foi: return AppliedBuffs.Any (b => b.Key.Equals (Name, StringComparison.OrdinalIgnoreCase)); um perfeito que não diferencia maiúsculas de minúsculas Contém um dicionário que diferencia maiúsculas de minúsculas.
David Burford
Estou tão tentado a negar isso. Cuidado, se você é o proprietário do dicionário, certamente não é esse o caminho. Use essa abordagem apenas se você não souber como o dicionário foi instanciado. Mesmo assim, para uma correspondência exata da sequência (não apenas da correspondência de partes), use a dict.Keys.Contains("bla", appropriate comparer)sobrecarga do LINQ para facilitar o uso.
nawfal
1

Acabei de encontrar o mesmo tipo de problema em que precisava de um dicionário caseINsensitive em um controlador ASP.NET Core.

Eu escrevi um método de extensão que faz o truque. Talvez isso possa ser útil para outros também ...

public static IDictionary<string, TValue> ConvertToCaseInSensitive<TValue>(this IDictionary<string, TValue> dictionary)
{
    var resultDictionary = new Dictionary<string, TValue>(StringComparer.InvariantCultureIgnoreCase);
    foreach (var (key, value) in dictionary)
    {
        resultDictionary.Add(key, value);
    }

    dictionary = resultDictionary;
    return dictionary;
}

Para usar o método de extensão:

myDictionary.ConvertToCaseInSensitive();

Em seguida, obtenha um valor do dicionário com:

myDictionary.ContainsKey("TheKeyWhichIsNotCaseSensitiveAnymore!");
Ferry Jongmans
fonte
0

Se você não tem controle na criação da instância, digamos que seu objeto seja desterilizado do json etc, você pode criar uma classe de wrapper que herda da classe do dicionário.

public class CaseInSensitiveDictionary<TValue> : Dictionary<string, TValue>
{
    public CaseInSensitiveDictionary() : base(StringComparer.OrdinalIgnoreCase){}
}
AG
fonte