Question

j'utilise un Dictionary<string, int> où le int est un décompte de la clé.

Maintenant, je dois accéder à la dernière clé insérée dans le dictionnaire, mais je n'en connais pas le nom.La tentative évidente :

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

ne fonctionne pas, parce que Dictionary.Keys n'implémente pas d'indexeur [].

Je me demande simplement s'il existe une classe similaire ?J'ai pensé à utiliser un Stack, mais cela ne stocke qu'une chaîne.Je pourrais maintenant créer ma propre structure puis utiliser un Stack<MyStruct>, mais je me demande s'il existe une autre alternative, essentiellement un dictionnaire qui implémente un indexeur [] sur les clés ?

Était-ce utile?

La solution

Comme @Falanwe le souligne dans un commentaire, faire quelque chose comme ça est Incorrect:

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

Toi ne devrait pas dépendent de l'ordre des clés dans un dictionnaire.Si vous avez besoin de commander, vous devez utiliser un Dictionnaire commandé, comme suggéré dans ce répondre.Les autres réponses sur cette page sont également intéressantes.

Autres conseils

Vous pouvez utiliser un Dictionnaire commandé.

Représente une collection de paires de clés / valeur accessibles par la clé ou l'index.

Un dictionnaire est une table de hachage, vous n'avez donc aucune idée de l'ordre d'insertion !

Si vous souhaitez connaître la dernière clé insérée, je vous suggère d'étendre le dictionnaire pour inclure une valeur LastKeyInserted.

Par exemple.:

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

}

Cependant, vous rencontrerez des problèmes lorsque vous utiliserez .Remove() donc pour surmonter cela, vous devrez conserver une liste ordonnée des clés insérées.

Pourquoi ne pas simplement étendre la classe de dictionnaire pour ajouter une dernière propriété insérée par clé.Quelque chose comme ce qui suit peut-être ?

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

Tu peux toujours faire ça :

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

Mais je ne le recommanderais pas.Rien ne garantit que la dernière clé insérée se trouvera à la fin du tableau.La commande des clés sur MSDN n’est pas précisé et est sujet à changement.Dans mon très bref test, cela semble être dans l'ordre d'insertion, mais vous feriez mieux de créer une comptabilité appropriée comme une pile - comme vous le suggérez (même si je ne vois pas la nécessité d'une structure basée sur votre autres instructions) - ou un cache à variable unique si vous avez juste besoin de connaître la dernière clé.

Je pense que vous pouvez faire quelque chose comme ça, la syntaxe peut être fausse, n'a pas utilisé C # depuis un moment pour obtenir le dernier élément

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

ou utilisez Max au lieu de Last pour obtenir la valeur maximale, je ne sais pas laquelle correspond le mieux à votre code.

Une alternative serait un Collection à clés si la clé est intégrée dans la valeur.

Créez simplement une implémentation de base dans une classe scellée à utiliser.

Donc pour remplacer Dictionary<string, int> (ce qui n'est pas un très bon exemple car il n'y a pas de clé claire pour 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];

Je suis d'accord avec la deuxième partie de la réponse de Patrick.Même si dans certains tests, il semble conserver l'ordre d'insertion, la documentation (et le comportement normal des dictionnaires et des hachages) indique explicitement que l'ordre n'est pas spécifié.

Vous demandez simplement des ennuis en fonction de l'ordre des clés.Ajoutez votre propre comptabilité (comme l'a dit Patrick, une seule variable pour la dernière clé ajoutée) pour être sûr.De plus, ne vous laissez pas tenter par toutes les méthodes telles que Last et Max sur le dictionnaire, car celles-ci sont probablement liées au comparateur de clés (je n'en suis pas sûr).

Si vous décidez d'utiliser un code dangereux susceptible d'être cassé, cette fonction d'extension récupérera une clé depuis un Dictionary<K,V> selon son indexation interne (qui pour Mono et .NET semble actuellement être dans le même ordre que celui obtenu en énumérant les Keys propriété).

Il est de loin préférable d'utiliser Linq : dict.Keys.ElementAt(i), mais cette fonction itérera O(N);ce qui suit est O (1) mais avec une pénalité de performances de réflexion.

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 façon dont vous avez formulé la question m'amène à croire que l'int dans le dictionnaire contient la "position" de l'élément dans le dictionnaire.À en juger par l'affirmation selon laquelle les clés ne sont pas stockées dans l'ordre dans lequel elles sont ajoutées, si cela est correct, cela signifierait que keys.Count (ou .Count - 1, si vous utilisez une base zéro) devrait toujours toujours le numéro de la dernière clé saisie ?

Si c'est correct, y a-t-il une raison pour laquelle vous ne pouvez pas utiliser Dictionary<int, string> afin de pouvoir utiliser mydict[ mydict.Keys.Count ] ?

Je ne sais pas si cela fonctionnerait car je suis presque sûr que les clés ne sont pas stockées dans l'ordre dans lequel elles sont ajoutées, mais vous pouvez convertir KeysCollection en liste, puis obtenir la dernière clé de la liste...mais ça vaudrait le coup d'y jeter un oeil.

La seule autre chose à laquelle je peux penser est de stocker les clés dans une liste de recherche et d'ajouter les clés à la liste avant de les ajouter au dictionnaire...ce n'est pas joli quand même.

Pour développer le message de Daniels et ses commentaires concernant la clé, puisque la clé est de toute façon intégrée dans la valeur, vous pouvez recourir à l'utilisation d'un KeyValuePair<TKey, TValue> comme valeur.La raison principale en est que, en général, la clé ne peut pas nécessairement être directement dérivée de la valeur.

Cela ressemblerait alors à ceci :

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

Pour utiliser cela comme dans l'exemple précédent, vous feriez :

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;

Vous pouvez également utiliser SortedList et son homologue générique.Ces deux classes et dans la réponse d'Andrew Peters mentionnée OrderedDictionary sont des classes de dictionnaire dans lesquelles les éléments sont accessibles par index (position) ainsi que par clé.Comment utiliser ces cours, vous pouvez trouver : Classe SortedList , Classe générique SortedList .

Un dictionnaire n'est peut-être pas très intuitif pour utiliser un index comme référence, mais vous pouvez effectuer des opérations similaires avec un tableau de Paire de valeurs clés:

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

Visual Studio Voix de l'utilisateur donne un lien vers implémentation générique de OrderedDictionary par dotmore.

Mais si vous avez uniquement besoin d'obtenir des paires clé/valeur par index et que vous n'avez pas besoin d'obtenir des valeurs par clés, vous pouvez utiliser une astuce simple.Déclarez une classe générique (je l'ai appelée ListArray) comme suit :

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

Vous pouvez également le déclarer avec des constructeurs :

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

Par exemple, vous lisez des paires clé/valeur à partir d'un fichier et souhaitez simplement les stocker dans l'ordre dans lequel elles ont été lues afin de les récupérer plus tard par index :

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

Comme vous l'avez peut-être remarqué, vous ne pouvez pas nécessairement avoir uniquement des paires clé/valeur dans votre ListArray.Les tableaux d'éléments peuvent être de n'importe quelle longueur, comme dans un tableau irrégulier.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top