Bir Sözlük.Anahtarlar Tuşuna sayısal bir dizin aracılığıyla erişme


160

Ben anahtarın sayısı Dictionary<string, int>olduğu bir yerde kullanıyorum int.

Şimdi, Sözlük içerisine son eklenen Anahtar'a erişmem gerekiyor, ancak adını bilmiyorum. Bariz girişim:

int LastCount = mydict[mydict.keys[mydict.keys.Count]];

çalışmaz, çünkü Dictionary.Keysbir [] -indexer uygulamıyor.

Sadece benzer bir sınıf var mı acaba? Bir yığın kullanmayı düşündüm, ama bu sadece bir dize saklar. Şimdi kendi kendi yapı oluşturmak ve sonra a kullanabilirsiniz Stack<MyStruct>, ama merak ediyorum başka bir alternatif, aslında anahtarlar üzerinde bir [] -indexer uygulayan bir Sözlük?


1
Bu değişkeni kutlarsanız ne olur?
Paul Prewett

Yanıtlar:


222

@Falanwe bir yorumda belirttiği gibi, böyle bir şey yapmak yanlıştır :

int LastCount = mydict.Keys.ElementAt(mydict.Count -1);

Sen olmamalıdır bir Sözlükte tuşların sırasına bağlıdır. Siparişe ihtiyacınız varsa , bu cevapta önerildiği gibi bir OrderedDictionary kullanmalısınız . Bu sayfadaki diğer cevaplar da ilginç.


1
HashTableSystem.Collections.ICollection 'ile çalışmıyor gibi görünüyor ' ElementAt 'için bir tanım içermiyor ve' System.Collections.ICollection 'türünde bir ilk argümanı kabul eden' ElementAt 'uzantı yöntemi bulunmuyor
v.oddou

ElementAtOrDefaultİstisnasız sürümle çalışmak için sürümü kullanabilirsiniz .
Tarık Özgün Güner

22
Bu kadar açık bir şekilde yanlış cevabın bu kadar kabul edildiğini ve onaylandığını görmek korkutucu. Bu yanlış çünkü Dictionary<TKey,TValue>belgelerin belirttiği gibi "içindeki anahtarların sırası Dictionary<TKey, TValue>.KeyCollectionbelirtilmedi." Sipariş tanımlanmamışsa, hangisinin son konumda olduğunu bilmenin hiçbir yolu yoktur ( mydict.Count -1)
Falanwe

Bunu benim şüphe teyidi arıyordu çünkü bu bana korkutucu ... ama yararlı olamaz sipariş üzerine saymak !!! @Falanwe
Charlie

3
Bazıları için sipariş alakalı değil - sadece tüm anahtarları geçtiğiniz gerçeği.
Royi Mindel

59

Bir OrderedDictionary kullanabilirsiniz .

Anahtar veya dizin tarafından erişilebilen bir anahtar / değer çifti koleksiyonunu temsil eder.


42
Erhm, 19 yukarı oydan sonra, hiç kimse OrderedDictionary hala anahtar endekse almak için izin vermedi söyledi?
Lazlo

1
Bir ile bir tamsayı indeksi değeri erişebilirler OrderedDictionary bir ile değil, System.Collections.Generic.SortedDictionary <TKey, TValue> indeks ihtiyacı TKey olmak
Maxence

OrderedDictionary adı, öğeleri eklendikleri sırayla korumak için bu toplama işleviyle ilgilidir. Bazı durumlarda, bir sıralama bir sıralama ile aynı anlama sahiptir, ancak bu koleksiyonda yoktur.
Sharunas Bielskis

18

Bir Sözlük Hash Tablosudur, bu yüzden ekleme sırası hakkında hiçbir fikriniz yoktur!

Son eklenen anahtarı bilmek istiyorsanız, sözlüğün LastKeyInserted değerini içerecek şekilde genişletilmesini öneririm.

Örneğin:

public MyDictionary<K, T> : IDictionary<K, T>
{
    private IDictionary<K, T> _InnerDictionary;

    public K LastInsertedKey { get; set; }

    public MyDictionary()
    {
        _InnerDictionary = new Dictionary<K, T>();
    }

    #region Implementation of IDictionary

    public void Add(KeyValuePair<K, T> item)
    {
        _InnerDictionary.Add(item);
        LastInsertedKey = item.Key;

    }

    public void Add(K key, T value)
    {
        _InnerDictionary.Add(key, value);
        LastInsertedKey = key;
    }

    .... rest of IDictionary methods

    #endregion

}

Ancak, .Remove()bunun üstesinden gelmek için kullandığınızda sorunla karşılaşırsınız , anahtarların sıralı bir listesini tutmanız gerekir.


8

Neden yalnızca eklenen bir son anahtar özellik eklemek için sözlük sınıfını genişletmiyorsunuz. Aşağıdaki gibi bir şey olabilir mi?

public class ExtendedDictionary : Dictionary<string, int>
{
    private int lastKeyInserted = -1;

    public int LastKeyInserted
    {
        get { return lastKeyInserted; }
        set { lastKeyInserted = value; }
    }

    public void AddNew(string s, int i)
    {
        lastKeyInserted = i;

        base.Add(s, i);
    }
}

2
LastKeyInserted öğesini son eklenen değere ayarlıyorsunuz. Ya en son eklenen anahtara ayarlamak istediniz veya değişken ve özellik için daha iyi adlara ihtiyacınız var.
Fantius

6

Bunu her zaman yapabilirsiniz:

string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]

Ama bunu tavsiye etmem. Son eklenen anahtarın dizinin sonunda olacağının garantisi yoktur. MSDN'deki Anahtarlar için sipariş belirtilmemiş ve değişebilir. Çok kısa testimde, yerleştirme sırası gibi görünüyor, ancak bir yığın gibi uygun defter tutma konusunda daha iyi olursunuz - önerdiğiniz gibi (gerçi sizin diğer ifadeler) - veya en son anahtarı bilmeniz gerekiyorsa tek değişkenli önbellek.


5

Böyle bir şey yapabileceğinizi düşünüyorum, sözdizimi yanlış olabilir, bir süre C # kullanmayın Son öğeyi almak için

Dictionary<string, int>.KeyCollection keys = mydict.keys;
string lastKey = keys.Last();

veya max değerini almak için Last yerine Max kullanın, hangisinin kodunuza daha iyi uyduğunu bilmiyorum.


2
"Last ()" bir uzantı yöntemi olduğundan, .NET Framework 3.5'e ihtiyacınız olduğunu ve "System.Linq kullanarak" .cs dosyanızın en üstüne eklemeniz gerektiğini ekleyeceğim.
SuperOli

Bunu son olarak deneyin (Dist <string, string> açıkçası kullanırken :-) KeyValuePair <string, string> last = oAuthPairs.Last (); eğer (kvp.Key! = last.Key) {_oauth_ParamString = _oauth_ParamString + "&"; }
Tim Windsor

4

Patrick'in cevabının ikinci kısmına katılıyorum. Bazı testlerde ekleme talimatını koruyor gibi görünse bile, belgeler (ve sözlükler ve karmalar için normal davranışlar) siparişin belirtilmediğini açıkça belirtir.

Sadece anahtarların sırasına bağlı olarak sorun istiyorsunuz. Emin olmak için kendi defter tutma işleminizi ekleyin (Patrick'in dediği gibi, son eklenen anahtar için yalnızca tek bir değişken). Ayrıca, muhtemelen anahtar karşılaştırıcıyla ilgili olduğu için sözlükte Last ve Max gibi tüm yöntemlere cazip gelmeyin (bundan emin değilim).


4

Bozulmaya maruz kalan tehlikeli kod kullanmaya karar vermeniz durumunda, bu uzantı işlevi Dictionary<K,V>dahili dizinine göre bir anahtardan bir anahtar getirecektir (Mono ve .NET için şu anda Keysözelliği numaralandırarak aldığınız sırayla görünüyor) ).

Linq: kullanmak çok tercih edilir dict.Keys.ElementAt(i), ancak bu fonksiyon O (N) 'i tekrarlar; aşağıdakiler O (1) 'dir ancak yansıma performansı cezası vardır.

using System;
using System.Collections.Generic;
using System.Reflection;

public static class Extensions
{
    public static TKey KeyByIndex<TKey,TValue>(this Dictionary<TKey, TValue> dict, int idx)
    {
        Type type = typeof(Dictionary<TKey, TValue>);
        FieldInfo info = type.GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance);
        if (info != null)
        {
            // .NET
            Object element = ((Array)info.GetValue(dict)).GetValue(idx);
            return (TKey)element.GetType().GetField("key", BindingFlags.Public | BindingFlags.Instance).GetValue(element);
        }
        // Mono:
        info = type.GetField("keySlots", BindingFlags.NonPublic | BindingFlags.Instance);
        return (TKey)((Array)info.GetValue(dict)).GetValue(idx);
    }
};

Hmm, cevabı iyileştirmek için düzenleme yapmak aşağı oy aldı. Kodun (açıkçası) iğrenç olduğunu ve buna göre düşünülmesi gerektiğini açıkça belirtmedim mi?
Glenn Slayden

4

Anahtar değere katıştırılmışsa alternatiflerden biri KeyedCollection olur.

Kullanmak için mühürlü bir sınıfta basit bir uygulama oluşturun.

Yani değiştirmek için Dictionary<string, int>(int için net bir anahtar olmadığı için çok iyi bir örnek değildir).

private sealed class IntDictionary : KeyedCollection<string, int>
{
    protected override string GetKeyForItem(int item)
    {
        // The example works better when the value contains the key. It falls down a bit for a dictionary of ints.
        return item.ToString();
    }
}

KeyedCollection<string, int> intCollection = new ClassThatContainsSealedImplementation.IntDictionary();

intCollection.Add(7);

int valueByIndex = intCollection[0];

Anahtar hakkındaki yorumlarınızla ilgili olarak, bu soruya verilen cevapma bakın.
takrl

3

Soruyu ifade etme şekliniz beni Sözlük'teki int öğesinin Sözlük'teki "konumunu" içerdiğine inanmamı sağlıyor. Anahtarların eklendikleri sıraya göre depolanmadığı iddiasına bakılırsa, bu doğruysa, bu anahtarların anlamına gelecektir. Sayı (veya sıfır tabanlı kullanıyorsanız .Count - 1) hala her zaman en son girilen anahtarın numarası olmalı?

Bu doğruysa, mydict [mydict.Keys.Count] kullanabilmeniz için Dictionary <int, string> kullanamamanızın bir nedeni var mı?


2

Bu işe yarayacak mı bilmiyorum, çünkü anahtarların eklendikleri sırada saklanmadığından eminim, ancak KeysCollection'ı bir Listeye atabilir ve listedeki son anahtarı alabilirsiniz ... ama bir göz atmaya değer.

Aklıma gelen tek şey, anahtarları bir arama listesinde saklamak ve sözlüğe eklemeden önce anahtarları listeye eklemek ... güzel tho değil.


@Juan: KeyCollection'da .Last () yöntemi yoktur
lomaxx

Ben kodu test etmedi, ama yöntem [MSDN] [1] üzerinde belgelenir belki başka bir sürümü çerçeve? [1]: msdn.microsoft.com/en-us/library/bb908406.aspx
Juan

2 yıl geç ama birileri yardımcı olabilir ... Juan'ın gönderisine aşağıdaki cevabımı görün. Last () bir uzantı yöntemidir.
SuperOli

2

Daniels mesajını ve anahtarla ilgili yorumlarını genişletmek için, anahtar yine de değerin içine gömüldüğünden, değer KeyValuePair<TKey, TValue>olarak a kullanmaya başvurabilirsiniz . Bunun temel nedeni, genel olarak, Anahtarın mutlaka doğrudan değerden türetilememesidir.

O zaman şöyle görünecektir:

public sealed class CustomDictionary<TKey, TValue>
  : KeyedCollection<TKey, KeyValuePair<TKey, TValue>>
{
  protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
  {
    return item.Key;
  }
}

Bunu önceki örnekte olduğu gibi kullanmak için:

CustomDictionary<string, int> custDict = new CustomDictionary<string, int>();

custDict.Add(new KeyValuePair<string, int>("key", 7));

int valueByIndex = custDict[0].Value;
int valueByKey = custDict["key"].Value;
string keyByIndex = custDict[0].Key;

2

Bir sözlük, referans için dizin kullanmak için çok sezgisel olmayabilir, ancak bir KeyValuePair dizisiyle benzer işlemlere sahip olabilirsiniz :

ex. KeyValuePair<string, string>[] filters;



1

Visual Studio Kullanıcı UserVoice dotmore tarafından genel OrderedDictionary uygulaması için bir bağlantı verir .

Ancak, yalnızca anahtar / değer çiftlerini dizine göre almanız ve değerleri anahtarlarla almanız gerekmiyorsa, basit bir hile kullanabilirsiniz. Bazı genel sınıf (ListArray olarak adlandırdım) aşağıdaki gibi bildirin:

class ListArray<T> : List<T[]> { }

Bunu yapıcılarla da bildirebilirsiniz:

class ListArray<T> : List<T[]>
{
    public ListArray() : base() { }
    public ListArray(int capacity) : base(capacity) { }
}

Örneğin, bir dosyadan bazı anahtar / değer çiftlerini okursunuz ve bunları daha sonra dizine göre almak için bunları okundukları sıraya koymak istersiniz:

ListArray<string> settingsRead = new ListArray<string>();
using (var sr = new StreamReader(myFile))
{
    string line;
    while ((line = sr.ReadLine()) != null)
    {
        string[] keyValueStrings = line.Split(separator);
        for (int i = 0; i < keyValueStrings.Length; i++)
            keyValueStrings[i] = keyValueStrings[i].Trim();
        settingsRead.Add(keyValueStrings);
    }
}
// Later you get your key/value strings simply by index
string[] myKeyValueStrings = settingsRead[index];

Fark etmiş olabileceğiniz gibi, ListArray öğenizde yalnızca anahtar / değer çiftleri olması gerekmez. Öğe dizileri, tırtıklı dizide olduğu gibi herhangi bir uzunlukta olabilir.

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.