Aceder a uma chave Dictionary.Keys através de um índice numérico

votos
136

Eu estou usando um Dictionary<string, int>onde a inté uma contagem da chave.

Agora, eu preciso acessar a última inserido chave dentro do dicionário, mas eu não sei o nome dele. A tentativa óbvia:

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

não funciona, porque Dictionary.Keysnão implementar um -indexer [].

Eu só quero saber se há alguma classe semelhante? Pensei em usar uma pilha, mas que armazena apenas uma corda. Eu poderia agora criar minha própria estrutura e, em seguida, usar um Stack<MyStruct>, mas eu me pergunto se não há outra alternativa, essencialmente, um dicionário que implementa uma -indexer [] nas Keys?

Publicado 07/08/2008 em 01:51
fonte usuário
Em outras línguas...                            


15 respostas

votos
204

Como @Falanwe aponta em um comentário, fazendo algo como isso é incorreta :

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

Você não deve depender da ordem de chaves em um dicionário. Se você precisa de ordenação, você deve usar um OrderedDictionary , como sugerido nesta resposta . As outras respostas nesta página são interessantes também.

Respondeu 19/01/2011 em 14:21
fonte usuário

votos
56

Você pode usar um OrderedDictionary .

Representa um conjunto de pares de chave / valor que são acessíveis por uma chave ou índice.

Respondeu 08/08/2008 em 02:23
fonte usuário

votos
16

Um dicionário é uma tabela hash, então você não tem idéia a ordem de inserção!

Se você quer saber a última chave inserida eu sugeriria estendendo o dicionário para incluir um valor LastKeyInserted.

Por exemplo:

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

}

Você vai ter problemas no entanto, quando você usa o .Remove()modo de superar isso, você terá que manter uma lista ordenada das chaves inseridas.

Respondeu 16/04/2009 em 15:09
fonte usuário

votos
8

Por que você não apenas estender a classe dicionário para adicionar em uma última chave de propriedade inserido. Algo como o seguinte, talvez?

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);
    }
}
Respondeu 07/08/2008 em 12:15
fonte usuário

votos
6

Você sempre pode fazer isso:

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

Mas eu não recomendo. Não há nenhuma garantia de que a última chave inserida será no final da matriz. A ordenação para Chaves no MSDN não é especificado, e sujeitas a alterações. Na minha breve teste, ele parece estar em ordem de inserção, mas você seria melhor fora de edifício em contabilidade adequada como uma pilha - como você sugere (embora eu não vejo a necessidade de uma estrutura com base no seu outras declarações) - ou único cache variável se você só precisa saber a chave mais recente.

Respondeu 07/08/2008 em 02:13
fonte usuário

votos
5

Eu acho que você pode fazer algo como isso, a sintaxe pode estar errado, não reembolsaram utilizado C # em quando para obter o último item

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

ou usar Max vez de Última para obter o valor máximo, eu não sei qual deles se encaixa o seu código melhor.

Respondeu 07/08/2008 em 02:18
fonte usuário

votos
4

Uma alternativa seria um KeyedCollection se a chave está embutido no valor.

Basta criar uma implementação básica de uma classe selada de usar.

Então, para substituir Dictionary<string, int>(que não é um bom exemplo, como não há uma chave clara para um 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];
Respondeu 20/07/2011 em 01:45
fonte usuário

votos
4

Eu concordo com a segunda parte da resposta de Patrick. Mesmo que em alguns testes, parece manter a ordem de inserção, a documentação (e comportamento normal para dicionários e hashes) afirma explicitamente a ordenação não é especificado.

Você está apenas pedindo para ter problemas, dependendo da ordenação das chaves. Adicione a sua própria contabilidade (como disse Patrick, apenas uma única variável para a última chave adicionado) para ter certeza. Além disso, não ser tentado por todos os métodos como a Última e Max no dicionário como essas são, provavelmente, em relação ao comparador chave (não tenho certeza sobre isso).

Respondeu 07/08/2008 em 03:38
fonte usuário

votos
3

No caso de você decidir usar códigos perigosos que está sujeito a ruptura, esta função de extensão vai buscar uma chave de um Dictionary<K,V>acordo com a sua indexação interna (que para Mono e .NET atualmente parece estar na mesma ordem que você começa por enumerar a Keyspropriedade ).

É muito preferível utilizar Linq: dict.Keys.ElementAt(i), mas esta função irá iterar O (N); o que se segue é o (1), mas com uma perda de desempenho reflexão.

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);
    }
};
Respondeu 12/04/2010 em 05:44
fonte usuário

votos
3

A maneira como você formulada a pergunta me leva a crer que o int no dicionário contém "posição" do item no dicionário. A julgar pela afirmação de que as chaves não são armazenadas na ordem em que elas são adicionadas, se isso é correto, isso significaria que keys.Count (ou .Count - 1, se você estiver usando-base zero) ainda deve sempre ser o número do último a entrar chave?

Se isso é correto, há alguma razão você não pode passar a usar Dictionary <int, string> de modo que você pode usar mydict [mydict.Keys.Count]?

Respondeu 07/08/2008 em 03:40
fonte usuário

votos
2

Um dicionário pode não ser muito intuitivo para usar o índice de referência, mas, você pode ter operações semelhantes com uma série de KeyValuePair :

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

Respondeu 06/04/2016 em 21:15
fonte usuário

votos
2

Você também pode usar SortedList e sua contraparte genérico. Estas duas classes e, em resposta Andrew Peters mencionado OrderedDictionary são classes de dicionário em que os itens podem ser acessados pelo índice (posição), bem como pela chave. Como usar essas classes você pode encontrar: SortedList classe , SortedList classe genérica .

Respondeu 25/03/2015 em 18:13
fonte usuário

votos
2

Para expandir a Daniels pós e seus comentários sobre a chave, uma vez que a chave é incorporado dentro do valor de qualquer maneira, você poderia recorrer ao uso de um KeyValuePair<TKey, TValue>como o valor. A principal razão para isto é que, em geral, a chave não é necessariamente directamente derivável a partir do valor.

Em seguida, ele ficaria assim:

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

Para usar isso como no exemplo anterior, você faria:

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;
Respondeu 20/07/2011 em 10:21
fonte usuário

votos
2

Eu não sei se isso iria funcionar porque eu tenho certeza que as chaves não são armazenadas na ordem em que são adicionados, mas você poderia lançar o KeysCollection a uma lista e, em seguida, obter a última chave na lista ... mas seria bom ter um olhar.

A única outra coisa que eu posso pensar é para armazenar as chaves em uma lista de pesquisa e adicione as chaves da lista antes de adicioná-los ao dicionário ... não é bonito tho.

Respondeu 07/08/2008 em 02:15
fonte usuário

votos
1

Visual Studio UserVoice dá um link para implementação OrderedDictionary genérico por dotmore.

Mas se você só precisa obter pares de chave / valor pelo índice e não precisam de obter valores de chaves, você pode usar um truque simples. Declarar alguma classe genérica (I chamou-ListArray) como segue:

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

Você também pode declará-la com construtores:

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

Por exemplo, você leu alguns pares de chave / valor de um arquivo e apenas deseja armazená-los na ordem em que foram lidos de modo a obter-los mais tarde pelo í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 você deve ter notado, você pode ter não necessariamente apenas pares de chave / valor em sua ListArray. As matrizes de item pode ser de qualquer comprimento, como na matriz irregulares.

Respondeu 03/11/2016 em 08:42
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more