Доступ к ключевому слову Dictionary.Keys с помощью числового индекса

Я использую Dictionary<string, int>, где int - это счетчик ключа.

Теперь мне нужно получить доступ к последнему вставленному ключу внутри Словаря, но я не знаю его имени. Очевидная попытка:

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

не работает, потому что Dictionary.Keys не реализует [] -indexer.

Мне просто интересно, есть ли подобный класс? Я думал об использовании Stack, но это только сохраняет строку. Теперь я мог бы создать свою собственную структуру, а затем использовать Stack<MyStruct>, но мне интересно, есть ли другая альтернатива, по существу Словарь, который реализует [] -индекс на клавишах?

Ответ 1

Как отмечает @Falanwe в комментарии, выполнение чего-то вроде неверно:

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

Вы не должны зависеть от порядка ключей в словаре. Если вам нужен заказ, вы должны использовать OrderedDictionary, как предлагается в этом answer. Другие ответы на этой странице также интересны.

Ответ 2

Вы можете использовать OrderedDictionary.

Представляет коллекцию ключей/значений пары, доступные ключом или индекс.

Ответ 3

Словарь - это хеш-таблица, поэтому вы не знаете порядок вставки!

Если вы хотите узнать последний вставленный ключ, я бы предложил расширить словарь, чтобы включить значение LastKeyInserted.

например:.

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

}

У вас возникнут проблемы, но при использовании .Remove(), чтобы преодолеть это, вам нужно будет сохранить упорядоченный список вставленных ключей.

Ответ 4

Почему бы вам просто не расширить класс словаря для добавления в последнем добавленном ключом свойстве. Может быть что-то вроде следующего?

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

Ответ 5

Вы всегда можете это сделать:

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

Но я бы не рекомендовал его. Там нет гарантии, что последний вставленный ключ будет в конце массива. Заказ для ключей в MSDN не указан и может быть изменен. В моем очень кратком тесте это похоже на порядок вставки, но вам лучше строить правильную бухгалтерию, такую ​​как стек, - как вы предлагаете (хотя я не вижу необходимости в структуре, основанной на вашем другие операторы) - или одиночный кеш переменной, если вам просто нужно знать последний ключ.

Ответ 6

Я думаю, что вы можете сделать что-то подобное, синтаксис может быть неправильным, havent использовал С# через некоторое время Чтобы получить последний элемент

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

или используйте Max вместо Last для получения максимального значения, я не знаю, какой из них лучше подходит вашему коду.

Ответ 7

Я согласен со второй частью ответа Патрика. Даже если в некоторых тестах он, похоже, сохраняет порядок вставки, документация (и нормальное поведение для словарей и хешей) явно заявляет, что порядок не указан.

Вы просто запрашиваете проблемы в зависимости от порядка ключей. Добавьте свою собственную учетную запись (как сказал Патрик, только одна переменная для последнего добавленного ключа). Кроме того, не искушайтесь всеми методами, такими как Last и Max в словаре, поскольку они, вероятно, связаны с ключевым компаратором (я не уверен в этом).

Ответ 8

Если вы решите использовать опасный код, подлежащий поломке, эта функция расширения будет извлекать ключ из Словаря в соответствии с его внутренней индексацией (что для Mono и .NET в настоящее время оказывается в том же порядке, что и вы путем перечисления свойства Keys).

Гораздо предпочтительнее использовать Linq: dict.Keys.ElementAt(i), но я не знаю, достаточно ли эта функция достаточно, чтобы не перебирать O (N). Ниже приведено O (1), но с оценкой эффективности отражения.

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

Ответ 9

Альтернативой может быть KeyedCollection, если ключ встроен в значение.

Просто создайте базовую реализацию в закрытом классе для использования.

Итак, заменим Dictionary<string, int> (что не очень хороший пример, поскольку для 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];

Ответ 10

То, как вы сформулировали этот вопрос, заставляет меня поверить, что int в словаре содержит элемент "позиция" в словаре. Судя по утверждению, что ключи не хранятся в том порядке, в котором они добавлены, если это правильно, это означает, что keys.Count(или .Count-1, если вы используете нулевое значение), все равно должны всегда будет номером последнего введенного ключа?

Если это правильно, есть ли какая-то причина, по которой вы не можете использовать Dictionary < int, string > так что вы можете использовать mydict [mydict.Keys.Count]?

Ответ 11

Я не знаю, будет ли это работать, потому что я уверен, что ключи не хранятся в том порядке, в котором они добавлены, но вы можете включить KeysCollection в список, а затем получить последний ключ в списке... но стоило бы взглянуть.

Единственное, что я могу придумать, это сохранить ключи в списке поиска и добавить ключи в список, прежде чем добавлять их в словарь... это не очень.

Ответ 12

Чтобы развернуть сообщение Дэниелса и его комментарии относительно ключа, так как ключ вложен в значение в любом случае, вы можете прибегнуть к использованию KeyValuePair<TKey, TValue> в качестве значения. Основная причина этого заключается в том, что, вообще говоря, ключ не обязательно выводится непосредственно из значения.

Тогда это будет выглядеть так:

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

Чтобы использовать это как в предыдущем примере, вы бы сделали:

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;

Ответ 13

Вы также можете использовать SortedList и его общий аналог. Эти два класса и в Andrew Peters ответили, что OrderedDictionary - это словарные классы, в которые можно получить доступ к элементам (положению), а также по ключу. Как использовать эти классы, вы можете найти: SortedList Class, СортировкаList Generic Class.

Ответ 14

Словарь может быть не очень интуитивным для использования индекса для ссылки, но вы можете иметь подобные операции с массивом KeyValuePair:

ех. KeyValuePair<string, string>[] filters;

Ответ 15

Visual Studio UserVoice дает ссылку на generic OrderedDictionary реализация по dotmore.

Но если вам нужно только получить пары ключ/значение по индексу и не нужно получать значения по ключам, вы можете использовать один простой трюк. Объявите некоторый общий класс (я назвал его ListArray) следующим образом:

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

Вы также можете объявить его с помощью конструкторов:

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

Например, вы читаете несколько пар ключ/значение из файла и просто хотите сохранить их в том порядке, в котором они были прочитаны, чтобы впоследствии получить их по индексу:

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

Как вы, возможно, заметили, вы можете не обязательно иметь пары ключей/значений в ListArray. Массивы элементов могут иметь любую длину, например, в массиве с зазубринами.