Доступ к ключевому слову 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. Массивы элементов могут иметь любую длину, например, в массиве с зазубринами.