2008-08-07 14 views
134

Estoy usando un Dictionary<string, int> donde el int es un conteo de la clave.Accediendo a una tecla Dictionary.Keys a través de un índice numérico

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 -indexer [].

Me pregunto si hay 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 existe otra alternativa, esencialmente un diccionario que implemente un [] -indexer en las teclas.

+1

¿Qué sucede si coloca esa variable en la casilla? –

Respuesta

203

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

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

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

+1

parece no funcionar con 'HashTable' System.Collections.ICollection' no contiene una definición para 'ElementAt' y no se puede encontrar ningún método de extensión 'ElementAt' que acepte un primer argumento de tipo 'System.Collections.ICollection' –

+0

Puede use la versión 'ElementAtOrDefault' para trabajar con una versión sin excepciones. –

+19

Da miedo ver aceptada y votada tanto una respuesta descaradamente errónea. Está mal porque, como la ['Dictionary ' documentación] (https://msdn.microsoft.com/en-us/library/yt2fy5zk (v = vs.110) .aspx) establece "El orden de las teclas en el 'Diccionario .KeyCollection' no está especificado." Al ser el orden indefinido, no hay manera de saber con certeza cuál está en la última posición ('mydict.Count -1') – Falanwe

6

Siempre se puede hacer esto:

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

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

2

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

La única otra cosa en la que puedo pensar es almacenar las claves en una lista de búsqueda y agregar las claves a la lista antes de agregarlas al diccionario ... no es muy bueno.

+0

No he probado el código, pero el método está documentado en [MSDN] [1] ¿Tal vez sea otra versión del framework? [1]: http://msdn.microsoft.com/en-us/library/bb908406.aspx – Juan

+0

@Juan: no hay ningún método .Last() en el KeyCollection – lomaxx

+0

2 años tarde pero podría ayudar a alguien ... ver mi respuesta a la publicación de Juan a continuación. Last() es un método de extensión. – SuperOli

5

Creo que se puede hacer algo como esto, la sintaxis podría estar equivocado, havent utilizado C# desde hace tiempo Para obtener el último elemento

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

o utilizar Max en lugar de pasada para obtener el valor máximo, No sé cuál se ajusta mejor a tu código.

+2

Agregaría que dado que "Last()" es un método de extensión, necesitaría .NET Framework 3.5 y agregará "using System.Linq" en la parte superior de su archivo .cs. – SuperOli

+0

Pruebe esto último (cuando se usa un Dist obviamente :-) KeyValuePair last = oAuthPairs.Last(); if (kvp.Key! = Last.Key) { _oauth_ParamString = _oauth_ParamString + "&"; } –

4

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 hash) establece explícitamente que el orden no está especificado.

Solo está solicitando problemas dependiendo del orden de las teclas. 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 se relacionen con el comparador de claves (no estoy seguro de eso).

3

La forma en que redactó la pregunta me lleva a creer que la 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 agregaron, si esto es correcto, eso significaría que keys.Count (o .Count - 1, si está usando cero) aún debería ¿Siempre será el número de la última clave ingresada?

Si eso es correcto, ¿hay alguna razón no se puede utilizar en su lugar diccionario < int, cadena > de modo que usted puede utilizar mydict [mydict.Keys.Count]?

8

¿Por qué no acaba de extender la clase diccionario para añadir en una propiedad insertada última tecla. 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); 
    } 
} 
+2

Está configurando lastKeyInserted al último valor insertado. O quiso configurarlo con la última clave insertada o necesita mejores nombres para la variable y la propiedad. – Fantius

+0

¿Eh? No, yo no estoy (?) – Calanus

56

Puede usar un OrderedDictionary.

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

+39

Erhm, después de 19 votaciones ascendentes, ¿nadie mencionó que OrderedDictionary todavía no permite obtener la clave por índice? – Lazlo

+0

Puede acceder a un valor con un índice entero con ** Dormir ordenado **, pero no con ** System.Collections.Generic.SortedDictionary ** donde el índice debe ser una TKey – Maxence

16

¡Un diccionario es una tabla hash, por lo que no tiene idea del orden de inserción!

Si quieres saber el último pasador insertado Yo sugeriría que se extiende al 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 

} 

que se ejecutará en problemas sin embargo, cuando se utiliza .Remove() modo de superar este tendrá que mantener una lista ordenada de las claves insertadas.

3

En caso de que decida utilizar código peligroso que está sujeto a rotura, esta función de extensión obtendrá una clave de Dictionary<K,V> de acuerdo con su indexación interna (que para Mono y .NET actualmente parece estar en el mismo orden que usted) obtener enumerando la propiedad Keys).

Es mucho preferible utilizar Linq: dict.Keys.ElementAt(i), pero que la 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); 
    } 
}; 
+0

Hmm, edición para mejorar la respuesta obtuvo un voto a favor.¿No dejé claro que el código es (obviamente) horrible y debería considerarse en consecuencia? –

4

Una alternativa sería un KeyedCollection si la llave se inserta en el valor.

Basta con crear una implementación básica de una clase cerrada de usar.

Así que para reemplazar Dictionary<string, int> (que no es un muy buen ejemplo, ya que no es una tecla de borrado 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]; 
+0

En cuanto a sus comentarios sobre la clave, consulte mi respuesta de seguimiento a esta. – takrl

2

Para ampliar Daniels puesto y sus observaciones sobre la clave, ya que la clave está incrustado dentro del valor de todos modos, se 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 directamente derivable 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; 
    } 
} 

utilizar esto como en el ejemplo anterior, podría 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

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

2

Un diccionario puede que no sea muy intuitiva para el uso de índice de referencia, pero, puede hacer que las operaciones similares con una serie de KeyValuePair:

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

1

Visual Studio's UserVoice da un enlace a generic OrderedDictionary implementation por dotmore.

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

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

También puede declarar con constructores:

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

Por ejemplo, puede leer algunos pares clave/valor de un archivo y simplemente desee almacenarlos en el orden en que fueron leídas por lo que para llegar más tarde por el í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 se habrán dado cuenta, puede hacer que no necesariamente sólo pares de clave/valor en su ListArray. Los conjuntos de elementos pueden ser de cualquier longitud, como en una matriz dentada.

Cuestiones relacionadas