汎用辞書の大文字と小文字を区別しないアクセス


244

マネージdllを使用するアプリケーションがあります。これらのdllの1つは、一般的な辞書を返します。

Dictionary<string, int> MyDictionary;  

辞書には、大文字と小文字のキーが含まれています。

別の側では、潜在的なキー(文字列)のリストを取得していますが、ケースを保証できません。キーを使用して辞書の値を取得しようとしています。しかし、もちろん大文字と小文字が一致しないため、次のコードは失敗します。

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

MSDNドキュメントに記載されているように、TryGetValueに大文字と小文字を区別しないフラグがあることを期待していましたが、これは一般的な辞書には無効のようです。

重要なケースを無視してその辞書の値を取得する方法はありますか?適切なStringComparer.OrdinalIgnoreCaseパラメータを使用してディクショナリの新しいコピーを作成するよりも良い回避策はありますか?


関連する投稿-C#c#
RBT

回答:


514

StringComparer値を取得しようとするポイントでaを指定する方法はありません。あなたはそれについて考える、場合"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);

次に、この新しいディクショナリはso でGetHashCode()実装を使用し、同じ値を提供します。StringComparer.OrdinalIgnoreCasecomparer.GetHashCode("foo")comparer.GetHashcode("FOO")

あるいは、ディクショナリにいくつかの要素しかない場合、および/または1回または2回ルックアップするだけでよい場合は、元のディクショナリをとして扱い、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を使用するのですか?
ジョーダン

まあ、それはあなたが望む行動に依存します。ユーザーがUIを介してキーを提供している場合(または、たとえばssとßが等しいことを考慮する必要がある場合)、別のカルチャを使用する必要がありますが、その値が、外部依存関係の場合、「OrdinalCulture」は合理的な仮定です。
Iain Galloway

1
default(KeyValuePair<T, U>)ありませんnull-それはだKeyValuePair場所Key=default(T)Value=default(U)。そのため?.、LINQの例では演算子を使用できません。つかむ必要がFirstOrDefault()あり、(この特定のケースでは)かどうかを確認しますKey == null
asherber

38

通常のディクショナリコンストラクターを使用したことのないLINQユーザーの場合:

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);
ヨルダン

6
「.NET Frameworkで文字列を使用するためのベストプラクティス」に従って、ではToUpperInvariantなくを使用してくださいToLowermsdn.microsoft.com/en-us/library/dd465121%28v=vs.110%29.aspx
Fred

これは私にとっては良かったです。キーをさかのぼってチェックする必要がありました。私はそれをもう少し合理化しましたvar item = MyDictionary.FirstOrDefault(x => x.Key.ToUpperInvariant() == keyValueToCheck.ToUpperInvariant());
ジェイ

なぜ dict.Keys.Contains("bla", appropriate comparer)ですか?さらに、C#のキー値ペアは構造体であるため、FirstOrDefaultのnullは取得されません。
nawfal
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.