Accediendo a un Diccionario.Claves Clave a través de un índice numérico


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

Ahora, necesito acceder a la última Tecla insertada dentro del Diccionario, pero no sé el nombre de la misma. El intento obvio:

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

No funciona, porque Dictionary.Keys no implementa un []- indexer.

Me pregunto si hay alguna clase similar? Pensé en usar una pila, pero que 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 implementa un [] - indexador en las Teclas?

Author: user2771704, 2008-08-07

15 answers

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

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

Usted no debería depender del orden de las claves en un diccionario. Si necesita ordenar, debe usar un OrderedDictionary, como se sugiere en esta respuesta. Las otras respuestas en esta página también son interesantes.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:53

Puede usar un OrderedDictionary.

Representa una colección de clave/valor pares que son accesibles por la tecla o índice.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-08 01:23:22

Un diccionario es una tabla Hash, ¡así que no tienes idea del orden de inserción!

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

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, se encontrará con problemas cuando use .Remove(), por lo que para superar esto tendrá que mantener una lista ordenada de las claves insertadas.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-06 20:19:42

¿Por qué no extiende la clase dictionary para agregar una propiedad de última clave insertada? ¿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);
    }
}
 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-11 12:46:41

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 Claves en MSDN no está especificado y está sujeto a cambios. En mi prueba muy breve, parece estar en orden de inserción, pero sería mejor construir en la contabilidad adecuada como una pila as como sugiere (aunque no veo la necesidad de una estructura basada en sus otras declaraciones) cache o caché de una sola variable si solo necesito saber la última clave.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-07 01:17:04

Creo que puedes hacer algo como esto, la sintaxis podría estar mal, 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 max, no sé cuál se ajusta mejor a su código.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-07 01:18:25

Una alternativa sería KeyedCollection si la clave está incrustada en el valor.

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

Así que 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];
 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-20 00:45:48

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 para diccionarios y hashes) indica explícitamente que el orden no está especificado.

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

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-11 16:57:02

En caso de que decida usar código peligroso que está sujeto a rotura, esta función de extensión obtendrá 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 obtiene al enumerar la propiedad Keys).

Es mucho preferible usar Linq: dict.Keys.ElementAt(i), pero esa función iterará O(N); lo siguiente es O(1) pero con una penalización de rendimiento de 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);
    }
};
 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-02 18:13:28

La forma en que redactó la pregunta me lleva a creer que el int en el Diccionario contiene la "posición" del ítem 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 las claves.Count (o .Count-1, si está utilizando basado en cero) todavía debe ser siempre el número de la última clave introducida?

Si eso es correcto, ¿hay alguna razón por la que no pueda usar Dictionary para que pueda usar mydict [mydict.Claves.Conde]?

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-07 02:40:52

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

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

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-07 01:15:55

Para ampliar la publicación de Daniels y sus comentarios con respecto a la clave, ya que la clave está incrustada dentro del valor de todos modos, podría recurrir a usar un KeyValuePair<TKey, TValue> como 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, debes hacer:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-20 09:35:50

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

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-25 18:13:00

Un diccionario puede no ser muy intuitivo para usar index como referencia, pero puede tener operaciones similares con una matriz de 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-06 21:15:54

La opción de usuario de Visual Studio proporciona un enlace a la implementación genérica de OrderedDictionary de 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 puede declararlo con constructores:

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

Por ejemplo, usted lee algunos pares clave / valor de un archivo y solo desea 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 puede haber notado, no necesariamente puede tener solo pares de clave/valor en su ListArray. Los arrays item pueden ser de cualquier longitud, como en el array dentado.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-03 08:58:42