जेनेरिक शब्दकोश के लिए केस असंवेदनशील पहुंच


244

मेरे पास एक एप्लिकेशन है जो प्रबंधित dll का उपयोग करता है। उन dlls में से एक एक सामान्य शब्दकोश लौटाते हैं:

Dictionary<string, int> MyDictionary;  

शब्दकोश में ऊपरी और निचले मामले के साथ चाबियाँ हैं।

दूसरी तरफ मुझे संभावित कुंजी (स्ट्रिंग) की एक सूची मिल रही है, हालांकि मैं मामले की गारंटी नहीं दे सकता। मैं कुंजियों का उपयोग करके शब्दकोश में मूल्य प्राप्त करने की कोशिश कर रहा हूं। लेकिन निश्चित रूप से निम्नलिखित विफल हो जाएगा क्योंकि मेरे पास एक मामला बेमेल है:

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

मैं उम्मीद कर रहा था कि TryGetValue पर एक अनदेखी मामला होगा MSDN डॉक में उल्लिखित ध्वज , लेकिन ऐसा लगता है कि यह सामान्य शब्दकोशों के लिए मान्य नहीं है।

क्या उस मामले के मूल्य को अनदेखा करने का एक तरीका महत्वपूर्ण मामला है? क्या उचित StringComparer.OrdinalIgnoreCase पैरामीटर के साथ शब्दकोश की एक नई प्रतिलिपि बनाने की तुलना में बेहतर समाधान है ?


जवाबों:


514

StringComparerउस बिंदु पर निर्दिष्ट करने का कोई तरीका नहीं है जहां आप एक मूल्य प्राप्त करने का प्रयास करते हैं। यदि आप इसके बारे में सोचते हैं, "foo".GetHashCode()और "FOO".GetHashCode()पूरी तरह से अलग हैं, तो कोई उचित तरीका नहीं है कि आप केस-संवेदी हैश मैप पर केस-असंवेदनशील लागू कर सकें।

हालाँकि, आप पहली बार प्रयोग करके केस-असंवेदनशील शब्दकोश बना सकते हैं: -

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

या किसी मौजूदा केस-संवेदी शब्दकोश की सामग्री के साथ एक नया केस-असंवेदनशील शब्दकोश बनाएं (यदि आपको यकीन है कि कोई मामला टकराना नहीं है) -

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

यह नया शब्दकोश तब और इतने GetHashCode()पर कार्यान्वयन का उपयोग करता हैStringComparer.OrdinalIgnoreCasecomparer.GetHashCode("foo")comparer.GetHashcode("FOO") आप एक ही मूल्य दे।

वैकल्पिक रूप से, यदि शब्दकोश में कुछ ही तत्व हैं, और / या आपको केवल एक या दो बार देखने की आवश्यकता है, तो आप मूल शब्दकोष को इस पर एक IEnumerable<KeyValuePair<TKey, TValue>>और बस पुनरावृति मान सकते हैं: -

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

या यदि आप पसंद करते हैं, तो 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;
  }
}

यह आपको एक नई डेटा संरचना बनाने की लागत बचाता है, लेकिन बदले में O (1) के बजाय लुकअप की लागत O (n) है।


वास्तव में यह समझ में आता है। स्पष्टीकरण के लिए बहुत बहुत धन्यवाद।
TocToc

1
पुराने शब्दकोश को इधर-उधर रखने का कोई कारण नहीं है और नए को त्वरित करना है क्योंकि किसी भी मामले-टकराव के कारण यह विस्फोट होगा। यदि आप जानते हैं कि आपको टक्कर नहीं मिलेगी, तो आप शुरू से ही असंवेदनशील स्थिति का उपयोग कर सकते हैं।
Rhys बेविलाक्वा

2
यह दस साल हो गया है कि मैं .NET का उपयोग कर रहा हूं और मुझे अब यह पता चला है !! करंट कल्चर की जगह आप ऑर्डिनल का इस्तेमाल क्यों करते हैं?
जॉर्डन

खैर, यह आपके इच्छित व्यवहार पर निर्भर करता है। यदि उपयोगकर्ता UI के माध्यम से कुंजी प्रदान कर रहा है (या यदि आपको उदाहरण के लिए ss और) बराबर पर विचार करने की आवश्यकता है) तो आपको एक अलग संस्कृति का उपयोग करने की आवश्यकता होगी, लेकिन यह देखते हुए कि मूल्य का उपयोग एक हैशमैप के लिए कुंजी के रूप में किया जा रहा है एक बाहरी निर्भरता, मुझे लगता है कि 'ऑर्डिनलकल्चर' एक उचित धारणा है।
इलेन गैलोवे

1
default(KeyValuePair<T, U>)नहीं है null- यह एक KeyValuePairकहाँ Key=default(T)और है Value=default(U)। तो आप ?.LINQ उदाहरण में ऑपरेटर का उपयोग नहीं कर सकते हैं ; आपको FirstOrDefault()यह देखने के लिए कि यह देखने के लिए और फिर (इस विशेष मामले के लिए) को पकड़ने की आवश्यकता होगी Key == null
अशर

38

आपके लिए ऐसे LINQers हैं जो कभी भी एक नियमित शब्दकोश निर्माता का उपयोग नहीं करते हैं:

myCollection.ToDictionary(x => x.PartNumber, x => x.PartDescription, StringComparer.OrdinalIgnoreCase)

8

यह बहुत सुंदर नहीं है, लेकिन यदि आप शब्दकोश के निर्माण को नहीं बदल सकते हैं, और आपको इसकी ज़रूरत है एक गंदा हैक, इस बारे में:

var item = MyDictionary.Where(x => x.Key.ToLower() == MyIndex.ToLower()).FirstOrDefault();
    if (item != null)
    {
        TheValue = item.Value;
    }

13
या बस यह: नया शब्दकोश <string, int> (otherDict, StringComparer.CurrentCultureIgnorezase);
जॉर्डन

6
इसके ToUpperInvariantबजाय ".NET फ्रेमवर्क में स्ट्रिंग्स का उपयोग करने के लिए सर्वोत्तम अभ्यास" के बजाय ToLowermsdn.microsoft.com/en-us/library/dd465121%28v=vs.110%29.aspx
फ्रेड

यह मेरे लिए अच्छा था, जहां मुझे असंवेदनशील तरीके से कुंजियों की जांच करनी थी। मैंने इसे थोड़ा और सुव्यवस्थित कियाvar item = MyDictionary.FirstOrDefault(x => x.Key.ToUpperInvariant() == keyValueToCheck.ToUpperInvariant());
Jay

सिर्फ क्यों नहीं dict.Keys.Contains("bla", appropriate comparer)? इसके अलावा, आप FirstOrDefault के लिए शून्य प्राप्त नहीं करेंगे क्योंकि C # में keyvaluepair एक संरचना है।
नवफाल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.