Genel sözlük için büyük / küçük harfe duyarsız erişim


244

Yönetilen dlls kullanan bir uygulama var. Bu dll'lerden biri genel bir sözlük döndürür:

Dictionary<string, int> MyDictionary;  

Sözlük büyük ve küçük harf içeren anahtarlar içerir.

Başka bir tarafta potansiyel anahtarların (dize) bir listesini alıyorum ancak davayı garanti edemiyorum. Ben tuşları kullanarak sözlükte değer almaya çalışıyorum. Ama bir vaka uyuşmazlığım olduğu için aşağıdakiler başarısız olacaktır:

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

TryGetValue MSDN belgesinde belirtildiği gibi görmezden bir durum bayrağı olacağını umuyordum , ama bu genel sözlükler için geçerli değil gibi görünüyor.

Anahtar sözlüğü görmezden gelerek sözlüğün değerini almanın bir yolu var mı? Uygun StringComparer.OrdinalIgnoreCase parametresiyle sözlüğün yeni bir kopyasını oluşturmaktan daha iyi bir çözüm var mı ?


İlgili mesajlar - C # , c #
RBT

Yanıtlar:


514

StringComparerBir değer elde etmeye çalıştığınız noktada a belirtmenin bir yolu yoktur . Bunu düşünürseniz "foo".GetHashCode()ve "FOO".GetHashCode()tamamen farklıysanız, büyük / küçük harfe duyarlı bir karma haritaya büyük / küçük harfe duyarlı olmayan bir uygulama uygulamak için makul bir yol yoktur.

Bununla birlikte, aşağıdakileri kullanarak ilk etapta büyük / küçük harfe duyarlı olmayan bir sözlük oluşturabilirsiniz: -

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

Veya mevcut büyük / küçük harfe duyarlı bir sözlüğün içeriği ile büyük / küçük harfe duyarlı olmayan bir sözlük oluşturun (büyük / küçük harfe çarpışma olmadığından eminseniz): -

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

Bu yeni sözlük ardından kullandığı GetHashCode()üzerinde uygulanmasını StringComparer.OrdinalIgnoreCaseyüzden comparer.GetHashCode("foo")ve comparer.GetHashcode("FOO")size aynı değeri verir.

Alternatif olarak, sözlükte yalnızca birkaç öğe varsa ve / veya yalnızca bir veya iki kez arama yapmanız gerekiyorsa, orijinal sözlüğü bir olarak ele alabilir IEnumerable<KeyValuePair<TKey, TValue>>ve üzerinde tekrarlayabilirsiniz: -

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

Veya LINQ olmadan tercih ederseniz: -

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

Bu, yeni bir veri yapısı oluşturma maliyetinden tasarruf etmenizi sağlar, ancak bunun karşılığında bir arama maliyeti O (1) yerine O (n) olur.


Gerçekten mantıklı. Açıklama için çok teşekkürler.
TocToc

1
Eski sözlüğü etrafında tutmak ve yenisini başlatmak için herhangi bir neden yoktur, çünkü herhangi bir vaka çarpışması patlamaya neden olur. Çarpışma olmayacağını biliyorsanız, başlangıçtan itibaren büyük / küçük harfe duyarsız da kullanabilirsiniz.
Rhys Bevilaqua

2
.NET kullandığım on yıl oldu ve şimdi bunu anladım !! CurrentCulture yerine neden Ordinal kullanıyorsunuz?
Ürdün

İstediğiniz davranışa bağlıdır. Kullanıcı anahtarı UI aracılığıyla sağlıyorsa (veya örneğin ss ve ß eşitini düşünmeniz gerekiyorsa), farklı bir kültür kullanmanız gerekir, ancak değerin, gelen bir hashmap için anahtar olarak kullanıldığı göz önüne alındığında harici bir bağımlılık, bence 'OrdinalCulture' makul bir varsayımdır.
Iain Galloway

1
default(KeyValuePair<T, U>)değil null- bu bir KeyValuePairyer Key=default(T)ve Value=default(U). Böylece ?.LINQ örneğinde operatörü kullanamazsınız ; tutup FirstOrDefault()ardından (bu özel durum için) olup olmadığını kontrol etmeniz gerekir Key == null.
asherber

38

Sizin için normal bir sözlük kurucusu kullanmayan LINQers sizin için:

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

8

Çok zarif değil ama sözlük oluşturmayı değiştiremezseniz ve ihtiyacınız olan tek şey kirli bir kesmek, buna ne dersiniz:

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

13
ya da sadece bu: new Dictionary <string, int> (otherDict, StringComparer.CurrentCultureIgnoreCase);
Ürdün

6
".NET Framework'te Dizeleri Kullanmak için En İyi Uygulamalar" ToUpperInvariantyerine kullanın ToLower. msdn.microsoft.com/tr-tr/library/dd465121%28v=vs.110%29.aspx
Fred

Anahtarları geriye dönük olarak duyarsız bir şekilde kontrol etmek zorunda kaldım, bu benim için iyi oldu. Biraz daha aerodinamik yaptımvar item = MyDictionary.FirstOrDefault(x => x.Key.ToUpperInvariant() == keyValueToCheck.ToUpperInvariant());
Jay

Neden sadece dict.Keys.Contains("bla", appropriate comparer)? Ayrıca, C # anahtar değerine sahip bir yapı olduğundan FirstOrDefault için boş olmaz.
nawfal
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.