Словарний нечутливий словник зі строковим ключем у C #


155

Якщо у мене Dictionary<String,...>є, чи можливо зробити такі методи, як ContainsKeyнечутливі до регістру?

Це здавалося пов’язаним, але я не зрозумів це належним чином: c # Словник: створення Ключового випадку нечутливого через декларації


5
Що не так з використанням StringComparer.InvariantCultureIgnoreCase? Він робить те, що говорить ...
leppie

5
Я ніколи про це не чув, звідси питання!
Містер Хлопчик


Це питання пов’язане, але не зовсім повторює це. Це вдається впоратися з існуючим словником. Я починаю з нового коду, тому відповіді тут краще підходять.
goodeye

Відповіді:


321

Це здавалося пов’язаним, але я не зрозумів це належним чином: c # Словник: створення Ключового випадку нечутливого через декларації

Це справді пов’язано. Рішення полягає в тому, щоб сказати екземпляру словника не використовувати стандартний метод порівняння рядків (який враховує регістри), а скористатися нечутливим до регістру. Це робиться за допомогою відповідного конструктора :

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

Конструктор очікує, IEqualityComparerщо говорить словнику, як порівнювати ключі.

StringComparer.InvariantCultureIgnoreCaseдає вам IEqualityComparerекземпляр, який порівнює рядки в нечутливому до регістру манері.


1
Ідеально, я якось упустив STL-подібний спосіб передачі компаратора в ctor.
Містер Хлопчик

7
Це ніколи не перестає мене дивувати, як я завжди знаходжу відповідь на будь-яке питання кодування тут! Можливо, я назву свого наступного кота після тебе, Конрад! :-)
Джеймі

10
Можливо, порівняння StringComparing.OrdinalIgnoreCase швидше порівняно з культурою: stackoverflow.com/questions/492799/…
Манушин Ігор

Як це зробити, якщо я не створюю словник, але намагаюся використовувати ContainsKey () для нечутливого до випадків відповідності ключа на існуючий словник, який я отримую як частину об'єкта?
Шрідхар Р Кулкарні

1
@ShridharRKulkarni Ви принципово не можете (ефективно). Логіка порівняння є основною частиною внутрішньої структури даних словника. Щоб дозволити це, контейнер повинен був би підтримувати декілька індексів для своїх даних, а словники цього не роблять.
Конрад Рудольф

21
var myDic = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
myDic.Add("HeLlo", "hi");

if (myDic.ContainsKey("hello"))
    Console.WriteLine(myDic["hello"]);

1
Фрагмент коду говорить все це. Дуже корисний. Не впевнений, чому ця публікація має настільки менше результатів порівняно з прийнятою відповіддю лише за запізнення на 3 хвилини.
RBT

14

Мало шансів, коли ваша справа зі словником, який витягується з сторонніх або зовнішніх dll. Використання linq

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


3
Це чудово працює. Слово застереження , хоча, якщо ви зміните Anyдо SingleOrDefaultвам не отримаєте nullназад , якщо він не існує, натомість ви отримаєте KeyValuePair як з ключем і встановлює null!
NibblyPig

1
ContainsЗдається, це дуже специфічний випадок, коли ви працювали в той час. Як більш загальну корисну відповідь, я вважаю, що Equalsце краще. І на цій самій ноті замість того, щоб дублювати рядок з ToLower(), було б ще краще використовувати StringComparison.xxxCase.
Суамер

1
це було дуже корисно, і позбавлення від ToLower, безумовно, є поліпшенням - моїм випадком використання було: return AppliedBuffs.Any (b => b.Key.Equals (Ім'я, StringComppare.OrdinalIgnoreCase)); ідеально нечутливий до регістру Міститься у словнику з урахуванням регістру.
Девід Берфорд

Я так спокусився спростувати це. Слово обережності, якщо ви власник словника, це, звичайно, не спосіб зробити це. Використовуйте цей підхід лише в тому випадку, якщо ви не знаєте, як був створений словник. Навіть тоді для точного відповідності рядків (а не лише частину відповідності) використовуйте dict.Keys.Contains("bla", appropriate comparer)LINQ перевантаження для зручності використання.
nawfal

1

Я просто зіткнувся з такою ж проблемою, коли мені потрібен словник caseINsensitive в контролері ASP.NET Core.

Я написав метод розширення, який робить трюк. Можливо, це може бути корисним і для інших ...

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

Щоб використовувати метод розширення:

myDictionary.ConvertToCaseInSensitive();

Потім отримайте значення зі словника за допомогою:

myDictionary.ContainsKey("TheKeyWhichIsNotCaseSensitiveAnymore!");

0

Якщо у вас немає контролю над створенням екземпляра, скажімо, ваш об’єкт дестерилізований з json тощо, ви можете створити клас обгортки, який успадковується від класу словника.

public class CaseInSensitiveDictionary<TValue> : Dictionary<string, TValue>
{
    public CaseInSensitiveDictionary() : base(StringComparer.OrdinalIgnoreCase){}
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.