Pregunta

estoy usando un Dictionary<string, int> donde el int es un recuento de la clave.

Ahora, necesito acceder a la última clave insertada dentro del Diccionario, pero no sé su nombre.El intento obvio:

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

no funciona, porque Dictionary.Keys no implementa un indexador [].

Me pregunto si existe alguna clase similar.Pensé en usar una pila, pero eso solo almacena una cadena.Ahora podría crear mi propia estructura y luego usar un Stack<MyStruct>, pero me pregunto si hay otra alternativa, esencialmente un Diccionario que implemente un indexador [] en las Claves.

¿Fue útil?

Solución

Como señala @Falanwe en un comentario, hacer algo como esto es incorrecto:

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

no debe Depende del orden de las claves en un diccionario.Si necesita realizar un pedido, debe utilizar un OrdenadoDiccionario, como se sugiere en este respuesta.Las otras respuestas en esta página también son interesantes.

Otros consejos

Puedes usar un OrdenadoDiccionario.

Representa una colección de pares de clave/valor a las que se puede acceder por la clave o el índice.

Un diccionario es una tabla hash, por lo que no tienes idea del orden de inserción.

Si desea conocer la última clave insertada, sugeriría ampliar el Diccionario para incluir un valor LastKeyInserted.

P.ej.:

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

}

Sin embargo, tendrás problemas cuando utilices .Remove() Entonces, para superar esto, deberá mantener una lista ordenada de las claves insertadas.

¿Por qué no simplemente extiende la clase de diccionario para agregar una propiedad insertada en la última clave?¿Algo como lo siguiente tal vez?

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

Siempre puedes hacer esto:

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

Pero no lo recomendaría.No hay garantía de que la última clave insertada esté al final de la matriz.El pedido de llaves en MSDN no está especificado y está sujeto a cambios.En mi breve prueba, parece estar en orden de inserción, pero sería mejor que construyeras una contabilidad adecuada como una pila, como sugieres (aunque no veo la necesidad de una estructura basada en tu otras declaraciones), o caché de una sola variable si solo necesita conocer la clave más reciente.

Creo que puedes hacer algo como esto, la sintaxis podría estar equivocada, no he usado C# en un tiempo para obtener el último elemento

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

o use Max en lugar de Last para obtener el valor máximo, no sé cuál se ajusta mejor a su código.

Una alternativa sería una Colección clave si la clave está incrustada en el valor.

Simplemente cree una implementación básica en una clase sellada para usar.

Entonces para reemplazar Dictionary<string, int> (que no es un muy buen ejemplo ya que no hay una clave clara para 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];

Estoy de acuerdo con la segunda parte de la respuesta de Patrick.Incluso si en algunas pruebas parece mantener el orden de inserción, la documentación (y el comportamiento normal de los diccionarios y hashes) indica explícitamente que el orden no está especificado.

Solo estás buscando problemas dependiendo del orden de las claves.Agregue su propia contabilidad (como dijo Patrick, solo una variable para la última clave agregada) para estar seguro.Además, no se deje tentar por todos los métodos como Last y Max en el diccionario, ya que probablemente estén relacionados con el comparador clave (no estoy seguro de eso).

En caso de que decida utilizar un código peligroso que esté sujeto a roturas, esta función de extensión recuperará una clave de un Dictionary<K,V> de acuerdo con su indexación interna (que para Mono y .NET actualmente parece estar en el mismo orden que se obtiene al enumerar los Keys propiedad).

Es mucho preferible utilizar Linq: dict.Keys.ElementAt(i), pero esa función iterará O(N);lo siguiente es O(1) pero con una penalización en el rendimiento de la reflexión.

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

La forma en que redactó la pregunta me lleva a creer que el int en el Diccionario contiene la "posición" del elemento en el Diccionario.A juzgar por la afirmación de que las claves no se almacenan en el orden en que se agregan, si esto es correcto, eso significaría que claves.Count (o .Count - 1, si está usando base cero) aún debería ¿Será siempre el número de la última clave introducida?

Si eso es correcto, ¿hay alguna razón por la que no puedas usar Dictionary<int, string> para poder usar mydict[ mydict.Keys.Count ]?

No sé si esto funcionaría porque estoy bastante seguro de que las claves no se almacenan en el orden en que se agregan, pero podrías convertir KeysCollection a una Lista y luego obtener la última clave de la lista...pero valdría la pena echarle un vistazo.

La única otra cosa que se me ocurre es almacenar las claves en una lista de búsqueda y agregarlas a la lista antes de agregarlas al diccionario...Aunque no es bonito.

Para ampliar la publicación de Daniels y sus comentarios con respecto a la clave, dado que la clave está incrustada dentro del valor de todos modos, puede recurrir al uso de un KeyValuePair<TKey, TValue> como el valor.El principal razonamiento para esto es que, en general, la clave no es necesariamente derivable directamente del valor.

Entonces se vería así:

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

Para usar esto como en el ejemplo anterior, harías:

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;

También puedes usar SortedList y su contraparte genérica.Estas dos clases y en la respuesta de Andrew Peters mencionada OrderedDictionary son clases de diccionario en las que se puede acceder a los elementos por índice (posición) y por clave.Cómo utilizar estas clases puedes encontrar: Clase de lista ordenada , Clase genérica SortedList .

Es posible que un diccionario no sea muy intuitivo para usar el índice como referencia, pero puede tener operaciones similares con una variedad de par clavevalor:

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

estudio visual Voz de usuario da un enlace a implementación genérica del Diccionario Ordenado por dotmore.

Pero si solo necesita obtener pares clave/valor por índice y no necesita obtener valores por claves, puede usar un truco simple.Declare alguna clase genérica (la llamé ListArray) de la siguiente manera:

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

También puedes declararlo con constructores:

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

Por ejemplo, lees algunos pares clave/valor de un archivo y solo quieres almacenarlos en el orden en que fueron leídos para obtenerlos más tarde por índice:

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

Como habrás notado, no necesariamente puedes tener solo pares de clave/valor en tu ListArray.Las matrices de elementos pueden tener cualquier longitud, como en una matriz irregular.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top