Domanda

Sto usando a Dictionary<string, int> dove il int è un conteggio della chiave.

Ora devo accedere all'ultima chiave inserita nel Dizionario, ma non ne conosco il nome.Il tentativo ovvio:

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

non funziona, perché Dictionary.Keys non implementa un []-indicizzatore.

Mi chiedo solo se esiste qualche lezione simile?Ho pensato di utilizzare uno Stack, ma memorizza solo una stringa.Ora potrei creare la mia struttura e quindi utilizzare a Stack<MyStruct>, ma mi chiedo se esiste un'altra alternativa, essenzialmente un dizionario che implementa un []-indexer sulle chiavi?

È stato utile?

Soluzione

Come sottolinea @Falanwe in un commento, fare qualcosa del genere lo è errato:

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

Voi non dovrebbe dipendono dall'ordine delle chiavi in ​​un dizionario.Se hai bisogno di ordinare, dovresti usare un Dizionario ordinato, come suggerito in questo risposta.Interessanti anche le altre risposte in questa pagina.

Altri suggerimenti

Puoi usare un Dizionario ordinato.

Rappresenta una raccolta di coppie chiave/valori accessibili dalla chiave o dall'indice.

Un dizionario è una tabella hash, quindi non hai idea dell'ordine di inserimento!

Se vuoi conoscere l'ultima chiave inserita, suggerirei di estendere il dizionario per includere un valore LastKeyInserted.

Per esempio.:

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

}

Tuttavia, durante l'utilizzo riscontrerai problemi .Remove() quindi per ovviare a questo dovrai tenere un elenco ordinato delle chiavi inserite.

Perché non estendi semplicemente la classe del dizionario per aggiungere l'ultima proprietà inserita nella chiave.Qualcosa come il seguente forse?

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

Potresti sempre fare questo:

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

Ma non lo consiglierei.Non esiste alcuna garanzia che l'ultima chiave inserita si trovi alla fine dell'array.L'ordinazione delle chiavi su MSDN non è specificato e soggetto a modifiche.Nel mio brevissimo test, sembra essere in ordine di inserimento, ma faresti meglio a costruire con una corretta contabilità come uno stack, come suggerisci (anche se non vedo la necessità di una struttura basata sul tuo altre istruzioni) o cache a variabile singola se hai solo bisogno di conoscere la chiave più recente.

Penso che tu possa fare qualcosa del genere, la sintassi potrebbe essere sbagliata, non ha usato C# da un po 'di tempo per ottenere l'ultimo articolo

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

oppure usa Max invece di Last per ottenere il valore massimo, non so quale si adatta meglio al tuo codice.

Un'alternativa sarebbe a Collezione con chiave se la chiave è incorporata nel valore.

Basta creare un'implementazione di base in una classe sigillata da utilizzare.

Quindi da sostituire Dictionary<string, int> (che non è un ottimo esempio in quanto non esiste una chiave chiara per un 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];

Sono d'accordo con la seconda parte della risposta di Patrick.Anche se in alcuni test sembra mantenere l'ordine di inserimento, la documentazione (e il comportamento normale per dizionari e hash) afferma esplicitamente che l'ordine non è specificato.

Stai solo cercando problemi a seconda dell'ordine delle chiavi.Aggiungi la tua contabilità (come ha detto Patrick, solo una singola variabile per l'ultima chiave aggiunta) per essere sicuro.Inoltre, non lasciarti tentare da tutti i metodi come Last e Max sul dizionario poiché probabilmente sono in relazione al comparatore di chiavi (non ne sono sicuro).

Nel caso in cui decidi di utilizzare un codice pericoloso soggetto a rottura, questa funzione di estensione preleverà una chiave da a Dictionary<K,V> in base alla sua indicizzazione interna (che per Mono e .NET attualmente sembra essere nello stesso ordine che si ottiene enumerando i Keys proprietà).

È molto preferibile utilizzare Linq: dict.Keys.ElementAt(i), ma quella funzione ripeterà O(N);quello che segue è O(1) ma con una penalizzazione delle prestazioni di riflessione.

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

Il modo in cui hai formulato la domanda mi porta a credere che l'int nel Dizionario contenga la "posizione" dell'elemento nel Dizionario.A giudicare dall'affermazione che le chiavi non vengono archiviate nell'ordine in cui vengono aggiunte, se ciò è corretto, ciò significherebbe che keys.Count (o .Count - 1, se stai utilizzando la base zero) dovrebbe comunque essere sempre il numero dell'ultima chiave inserita?

Se è corretto, c'è qualche motivo per cui non puoi invece utilizzare Dictionary<int, string> in modo da poter utilizzare mydict[ mydict.Keys.Count ]?

Non so se funzionerebbe perché sono abbastanza sicuro che le chiavi non vengono archiviate nell'ordine in cui vengono aggiunte, ma potresti trasmettere KeysCollection a un List e quindi ottenere l'ultima chiave nell'elenco...ma varrebbe la pena dare un'occhiata.

L'unica altra cosa che mi viene in mente è memorizzare le chiavi in ​​un elenco di ricerca e aggiungerle all'elenco prima di aggiungerle al dizionario...non è carino però.

Per espandere il post di Daniels e i suoi commenti riguardanti la chiave, poiché la chiave è comunque incorporata nel valore, potresti ricorrere all'utilizzo di a KeyValuePair<TKey, TValue> come valore.Il motivo principale di ciò è che, in generale, la chiave non è necessariamente direttamente derivabile dal valore.

Allora sarebbe simile a questo:

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

Per usarlo come nell'esempio precedente, dovresti fare:

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;

Puoi anche utilizzare SortedList e la sua controparte generica.Queste due classi e nella risposta di Andrew Peters menzionate OrderedDictionary sono classi di dizionario in cui è possibile accedere agli elementi tramite indice (posizione) e tramite chiave.Come utilizzare queste classi puoi trovare: Classe SortedList , Classe generica SortedList .

Un dizionario potrebbe non essere molto intuitivo per l'utilizzo dell'indice come riferimento, ma è possibile eseguire operazioni simili con un array di Coppia valorichiave:

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

di Visual Studio UserVoice fornisce un collegamento a implementazione generica di OrderedDictionary di dotmore.

Ma se hai solo bisogno di ottenere coppie chiave/valore per indice e non hai bisogno di ottenere valori per chiavi, puoi usare un semplice trucco.Dichiara una classe generica (l'ho chiamata ListArray) come segue:

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

Puoi anche dichiararlo con i costruttori:

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

Ad esempio, leggi alcune coppie chiave/valore da un file e desideri semplicemente memorizzarle nell'ordine in cui sono state lette in modo da ottenerle successivamente tramite indice:

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

Come avrai notato, non puoi avere necessariamente solo coppie chiave/valore nel tuo ListArray.Gli array di elementi possono avere qualsiasi lunghezza, come nell'array frastagliato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top