Słownik z rozróżnianiem wielkości liter z typem klucza ciągu w języku C #

155

Jeśli mam a, Dictionary<String,...>czy jest możliwe, aby metody takie jak nie ContainsKeyuwzględniały wielkości liter?

Wydawało się, że jest to powiązane, ale nie zrozumiałem tego poprawnie: Słownik c #: sprawianie, że klucz nie uwzględnia wielkości liter poprzez deklaracje

Mr. Boy
źródło
5
Co jest złego w używaniu StringComparer.InvariantCultureIgnoreCase? Robi to, co mówi ...
leppie
5
Nigdy o tym nie słyszałem, stąd pytanie!
Mr. Boy
2
Możliwy duplikat dostępu bez
rozróżniania
To pytanie jest powiązane, ale nie jest jego duplikatem. Ta osoba dowiaduje się, jak radzić sobie z istniejącym słownikiem. Zaczynam od nowego kodu, więc odpowiedzi tutaj są lepiej dopasowane.
goodeye,

Odpowiedzi:

321

Wydawało się, że jest to powiązane, ale nie zrozumiałem tego poprawnie: Słownik c #: sprawianie, że klucz nie uwzględnia wielkości liter poprzez deklaracje

To jest rzeczywiście powiązane. Rozwiązaniem jest poinstruowanie instancji słownika, aby nie korzystała ze standardowej metody porównywania ciągów (która jest wrażliwa na wielkość liter), ale raczej używa metody niewrażliwej na wielkość liter. Odbywa się to za pomocą odpowiedniego konstruktora :

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

Konstruktor oczekuje instrukcji, IEqualityComparerktóra mówi słownikowi, jak porównywać klucze.

StringComparer.InvariantCultureIgnoreCasedaje IEqualityComparerinstancję, która porównuje ciągi znaków bez uwzględniania wielkości liter.

Konrad Rudolph
źródło
1
Idealnie, w jakiś sposób przegapiłem sposób przekazywania komparatora do ctora, podobny do STL.
Mr. Boy
7
Nigdy nie przestaje mnie zadziwiać, jak zawsze znajduję tutaj odpowiedź na każde pytanie dotyczące kodowania! Może nazwij mojego kolejnego kota po tobie, Konradzie! :-)
Jamie
10
To możliwe, metoda porównująca StringComparison.OrdinalIgnoreCase szybsza niż oparta na kulturze: stackoverflow.com/questions/492799/ ...
Manushin Igor
Jak to zrobić, jeśli nie tworzę słownika, ale próbuję użyć ContainsKey () do dopasowania klucza bez uwzględniania wielkości liter w istniejącym słowniku, który otrzymuję jako część obiektu?
Shridhar R Kulkarni
1
@ShridharRKulkarni Zasadniczo nie możesz (efektywnie). Logika porównania jest podstawową częścią wewnętrznej struktury danych słownika. Aby to umożliwić, kontener musiałby przechowywać wiele indeksów dla swoich danych, a słowniki tego nie robią.
Konrad Rudolph
21
var myDic = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
myDic.Add("HeLlo", "hi");

if (myDic.ContainsKey("hello"))
    Console.WriteLine(myDic["hello"]);
Steve
źródło
1
Fragment kodu mówi wszystko. Bardzo pomocne. Nie jestem pewien, dlaczego ten post ma tak mniej głosów pozytywnych w porównaniu z zaakceptowaną odpowiedzią tylko za spóźnienie o 3 minuty.
RBT
14

Istnieje kilka przypadków, w których masz do czynienia ze słownikiem, który jest pobierany z zewnętrznego lub zewnętrznego dll. Korzystanie z linq

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

Kurkula
źródło
3
To działa świetnie. Jednak słowo ostrzeżenia, jeśli zmienisz Anyna SingleOrDefaultsiebie, nie nullwrócisz, jeśli nie istnieje, zamiast tego otrzymasz parę kluczy z kluczem i wartością ustawioną na null!
NibblyPig
1
ContainsWygląda na bardzo specyficzny przypadek użycia czegoś, nad czym pracowałeś w tym czasie. Myślę, że jako bardziej ogólna pomocna odpowiedź Equalsjest lepsza. I w tej samej notatce, zamiast powielać ciąg z ToLower(), byłoby jeszcze lepiej użyć StringComparison.xxxCase.
Suamere,
1
było to bardzo przydatne, a pozbycie się ToLower jest zdecydowanie ulepszeniem - mój przypadek użycia to: return AppliedBuffs.Any (b => b.Key.Equals (Name, StringComparison.OrdinalIgnoreCase)); doskonały bez rozróżniania wielkości liter Zawiera słownik uwzględniający wielkość liter.
David Burford,
Jestem tak kuszony, że to odrzucam. Uwaga, jeśli jesteś właścicielem słownika, z pewnością nie jest to sposób na zrobienie tego. Używaj tej metody tylko wtedy, gdy nie wiesz, jak utworzono instancję słownika. Nawet wtedy, aby uzyskać dokładne dopasowanie ciągu (nie tylko częściowe dopasowanie), użyj dict.Keys.Contains("bla", appropriate comparer)przeciążenia LINQ w celu ułatwienia użycia.
nawfal
1

Właśnie napotkałem ten sam problem, w którym potrzebowałem słownika uwzględniającego wielkość liter w kontrolerze ASP.NET Core.

Napisałem metodę rozszerzającą, która załatwia sprawę. Może to może być pomocne również dla innych ...

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

Aby skorzystać z metody rozszerzenia:

myDictionary.ConvertToCaseInSensitive();

Następnie pobierz wartość ze słownika zawierającą:

myDictionary.ContainsKey("TheKeyWhichIsNotCaseSensitiveAnymore!");
Ferry Jongmans
źródło
0

Jeśli nie masz żadnej kontroli nad tworzeniem instancji, powiedzmy, że Twój obiekt jest zdesteryylowany z json itp., Możesz utworzyć klasę opakowania, która dziedziczy po klasie słownika.

public class CaseInSensitiveDictionary<TValue> : Dictionary<string, TValue>
{
    public CaseInSensitiveDictionary() : base(StringComparer.OrdinalIgnoreCase){}
}
AG
źródło