일반 사전에 대소 문자를 구분하지 않는 액세스


244

관리되는 dll을 사용하는 응용 프로그램이 있습니다. 해당 dll 중 하나가 일반 사전을 반환합니다.

Dictionary<string, int> MyDictionary;  

사전에는 대소 문자가 포함 된 키가 포함되어 있습니다.

다른 측면에서 잠재적 인 키 (문자열) 목록을 얻었지만이 경우를 보장 할 수는 없습니다. 키를 사용하여 사전의 값을 얻으려고합니다. 그러나 사례가 일치하지 않기 때문에 물론 다음이 실패합니다.

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

TryGetValue가 MSDN doc 에서 언급 한 것과 같이 무시 사례 플래그를 가지기를 희망 했지만 일반 사전에는 유효하지 않은 것 같습니다.

주요 사례를 무시하고 해당 사전의 가치를 얻는 방법이 있습니까? 적절한 StringComparer.OrdinalIgnoreCase 매개 변수 를 사용하여 사전의 새 사본을 작성하는 것보다 더 나은 해결 방법이 있습니까?


답변:


514

StringComparer값을 얻으려고 할 때 지점 을 지정할 방법이 없습니다 . 당신이 그것에 대해 생각 "foo".GetHashCode()하고 "FOO".GetHashCode()완전히 다르므로 대소 문자를 구분하지 않는 해시 맵에서 대소 문자를 구분하지 않는 get을 구현할 수있는 합리적인 방법이 없습니다.

그러나 먼저 다음을 사용하여 대소 문자를 구분하지 않는 사전을 작성할 수 있습니다.

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.OrdinalIgnoreCase있도록 comparer.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 Bevilaqua

2
.NET을 사용한 지 10 년이되었으며 이제는 이것을 알아 냈습니다. CurrentCulture 대신 Ordinal을 사용하는 이유는 무엇입니까?
Jordan

글쎄, 그것은 당신이 원하는 행동에 달려 있습니다. 사용자가 UI를 통해 키를 제공하는 경우 (또는 ss와 ß equal을 고려해야하는 경우) 다른 문화권을 사용해야하지만 값이 해시 맵의 키로 사용되는 경우 외부 의존성 때문에 'OrdinalCulture'는 합리적인 가정이라고 생각합니다.
Iain Galloway

1
default(KeyValuePair<T, U>)아니다 null- 그것은 A의 KeyValuePair경우 Key=default(T)Value=default(U). 따라서 ?.LINQ 예제 에서는 연산자를 사용할 수 없습니다 . 당신은 그것을 잡고 FirstOrDefault()(이 특별한 경우) 경우를 확인해야합니다 Key == null.
asherber

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
또는 이것 만 : new Dictionary <string, int> (otherDict, StringComparer.CurrentCultureIgnoreCase);
Jordan

6
".NET Framework에서 문자열을 사용하는 모범 사례"에 따라 ToUpperInvariant대신을 사용하십시오 ToLower. msdn.microsoft.com/ko-kr/library/dd465121%28v=vs.110%29.aspx
Fred

이것은 소감없이 키를 회고 적으로 점검 해야하는 곳에서 나에게 좋았습니다. 조금 더 간소화했습니다var item = MyDictionary.FirstOrDefault(x => x.Key.ToUpperInvariant() == keyValueToCheck.ToUpperInvariant());
Jay

왜 안돼 dict.Keys.Contains("bla", appropriate comparer)? 또한 C #의 키 값 쌍이 구조체이므로 FirstOrDefault에 대해 null을 얻지 못합니다.
nawfal
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.