Dostęp do słownika.Klawisze Klawisz przez indeks numeryczny

Używam Dictionary<string, int> Gdzie int jest licznikiem klucza.

Teraz muszę uzyskać dostęp do ostatnio wstawionego klucza w słowniku, ale nie znam jego nazwy. Oczywista próba:

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

Nie działa, ponieważ Dictionary.Keys nie implementuje indeksatora [].

Zastanawiam się tylko, czy jest jakaś podobna Klasa? Myślałem o użyciu stosu, ale to przechowuje tylko ciąg. Mógłbym teraz stworzyć własną strukturę, a następnie użyć Stack<MyStruct>, ale zastanawiam się, czy jest inna alternatywa, zasadniczo słownik, który implementuje indeks [] na kluczach?
Author: user2771704, 2008-08-07

15 answers

Jak zauważa @Falanwe w komentarzu, robienie czegoś takiego jest niepoprawne:

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

Ty nie powinieneś zależeć od kolejności kluczy w słowniku. Jeśli potrzebujesz zamówić, należy użyć OrderedDictionary , jak sugerowano w tej odpowiedź . Inne odpowiedzi na tej stronie są również interesujące.

 205
Author: Vitor Hugo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:53

Możesz użyć OrderedDictionary .

Reprezentuje zbiór klucza / wartości pary, które są dostępne za pomocą klucza lub indeks.

 56
Author: Andrew Peters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 01:23:22

Słownik jest tabelą Hash, więc nie masz pojęcia o kolejności wstawiania!

Jeśli chcesz znać ostatni wstawiony klucz, sugerowałbym rozszerzenie słownika o wartość LastKeyInserted.

Np.:

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

}

Napotkasz jednak problemy podczas używania .Remove(), więc aby to przezwyciężyć, będziesz musiał zachować uporządkowaną listę kluczy włożonych.

 17
Author: Sam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-06 20:19:42

Dlaczego po prostu nie rozszerzysz klasy słownika, aby dodać ostatnią włożoną właściwość klucza. Może coś takiego?

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);
    }
}
 8
Author: Calanus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-11 12:46:41

Zawsze możesz to zrobić:

string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]
Ale nie polecam. Nie ma gwarancji, że ostatni włożony klucz będzie na końcu tablicy. Kolejność kluczy w MSDN jest nieokreślona i może ulec zmianie. W moim bardzo krótkim teście wydaje się, że jest to kolejność wstawiania, ale lepiej byłoby zbudować w odpowiedniej księgowości jak stos-jak sugerujesz (choć nie widzę potrzeby struktury opartej na innych instrukcjach) -- lub single variable cache, jeśli wystarczy znać najnowszy klucz.
 6
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-07 01:17:04

Myślę, że można coś takiego zrobić, składnia może być błędna, dawno nie używałem C# Aby uzyskać ostatnią pozycję

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

Lub użyj Max zamiast Last, aby uzyskać wartość max, Nie wiem, który z nich lepiej pasuje do Twojego kodu.

 5
Author: Juan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-07 01:18:25

Alternatywą może być KeyedCollection Jeśli klucz jest osadzony w wartości.

Wystarczy utworzyć podstawową implementację w zamkniętej klasie do użycia.

Więc zastąpić Dictionary<string, int> (co nie jest dobrym przykładem, ponieważ nie ma jasnego klucza dla int).

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];
 4
Author: Daniel Ballinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-20 00:45:48

Zgadzam się z drugą częścią odpowiedzi Patryka. Nawet jeśli w niektórych testach wydaje się, że zachowuje porządek wstawiania, dokumentacja (i normalne zachowanie słowników i skrótów) wyraźnie stwierdza, że kolejność jest nieokreślona.

Prosisz o kłopoty w zależności od kolejności kluczy. Dodaj własną księgowość (jak powiedział Patrick, tylko jedną zmienną dla ostatnio dodanego klucza), aby mieć pewność. Również nie daj się skusić na wszystkie metody takie jak Last I Max w słowniku jako są one prawdopodobnie związane z komparatorem kluczy (nie jestem tego pewien).

 4
Author: Stephen Pellicer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-11 16:57:02

W przypadku, gdy zdecydujesz się użyć niebezpiecznego kodu, który może ulec uszkodzeniu, ta funkcja rozszerzenia pobierze klucz z Dictionary<K,V> zgodnie z jego wewnętrznym indeksowaniem (który dla Mono i. NET wydaje się być w tej samej kolejności, co Ty, wyliczając właściwość Keys).

Jest znacznie lepiej używać Linq: dict.Keys.ElementAt(i), ale ta funkcja będzie iterować O(N); poniżej jest O (1), ale z karą wydajności odbicia.

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);
    }
};
 4
Author: Glenn Slayden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-02 18:13:28

Sposób sformułowania pytania prowadzi mnie do przekonania, że int w słowniku zawiera "pozycję" pozycji w słowniku. Sądząc po twierdzeniu, że klucze nie są przechowywane w kolejności, w jakiej zostały dodane, jeśli jest to poprawne, oznacza to, że klucze.Hrabia (lub .Count-1, Jeśli używasz zero-based) czy nadal powinna być Liczba ostatnio wprowadzonego klucza?

Jeśli to prawda, czy jest jakiś powód, dla którego nie możesz użyć słownika, aby móc używać mydict[ mydict.Klucze.Hrabia ]?

 3
Author: Jeremy Privett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-07 02:40:52

Nie wiem, czy to zadziała, ponieważ jestem prawie pewien, że klucze nie są przechowywane w kolejności, w jakiej zostały dodane, ale możesz wrzucić KeysCollection do listy, a następnie uzyskać ostatni klucz na liście... ale warto zajrzeć.

Jedyną inną rzeczą, o której mogę myśleć, jest przechowywanie kluczy na liście wyszukiwania i dodawanie kluczy do listy przed dodaniem ich do słownika... to nie jest zbyt piękne.

 2
Author: lomaxx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-07 01:15:55

Aby rozwinąć Post Danielsa i jego komentarze dotyczące Klucza, ponieważ klucz jest osadzony w wartości, możesz użyć KeyValuePair<TKey, TValue> jako wartości. Głównym powodem tego jest to, że ogólnie klucz nie musi być bezpośrednio wyprowadzany z wartości.

Wtedy wyglądałoby to tak:

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

Aby użyć tego, jak w poprzednim przykładzie, wykonasz:

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
Author: takrl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-20 09:35:50

Możesz również użyć SortedList i jego ogólnego odpowiednika. Te dwie klasy i w Andrew Peters answer wymienione OrderedDictionary są klasami słownikowymi, w których elementy mogą być dostępne zarówno po indeksie (pozycji), jak i po kluczu. Jak korzystać z tych klas można znaleźć: SortedList Class , SortedList Generic Class .

 2
Author: Sharunas Bielskis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-25 18:13:00

Słownik może nie być zbyt intuicyjny w użyciu indeksu dla odniesienia, ale możesz mieć podobne operacje z tablicą KeyValuePair :

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

 2
Author: espaciomore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-06 21:15:54

Visual Studio ' S UserVoicedaje link do implementacji generic OrderedDictionary by dotmore.

Ale jeśli potrzebujesz tylko uzyskać pary klucz / wartość według indeksu i nie musisz pobierać wartości według kluczy, możesz użyć jednej prostej sztuczki. Zadeklaruj jakąś klasę generyczną (nazwałem ją ListArray) w następujący sposób:

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

Można również zadeklarować za pomocą konstruktorów:

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

Na przykład, odczytujesz parę klucz / wartość z pliku i chcesz je zapisać w takiej kolejności, w jakiej są zostały odczytane tak, aby później je zdobyć przez indeks:

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

Jak być może zauważyłeś, możesz mieć niekoniecznie tylko pary klucza / wartości w liście. Tablice elementów mogą mieć dowolną długość, jak w tablicy postrzępionej.

 1
Author: quicktrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-03 08:58:42