Как получить предыдущий ключ из SortedDictionary?
У меня есть словарь, содержащий пары ключевых значений.
SortedDictionary<int,int> dictionary=new SortedDictionary<int,int>();
dictionary.Add(1,33);
dictionary.Add(2,20);
dictionary.Add(4,35);
Я хочу получить предыдущую пару значений ключа из известного значения ключа. В приведенном выше случае, если у меня есть ключ 4, то как я могу получить <2,20>
?
Ответы
Ответ 1
Трудно эффективно реализовать это с помощью SortedDictionary<TKey, TValue>
, поскольку он реализован как двоичное дерево поиска, которое не отображает предшественников или преемников.
Конечно, вы можете просто перечислить каждый KeyValuePair, пока не найдете "известный" ключ. С небольшим количеством LINQ это будет выглядеть (предполагая, что ключ определенно существует и не является первым ключом):
SortedDictionary<int, int> dictionary = ...
int knownKey = ...
var previousKvp = dictionary.TakeWhile(kvp => kvp.Key != knownKey)
.Last();
Если эти предположения не выполняются, вы можете сделать:
var maybePreviousKvp = dictionary.TakeWhile(kvp => kvp.Key != knownKey)
.Cast<KeyValuePair<int, int>?>()
.LastOrDefault();
(Убедитесь, что maybePreviousKvp != null
, чтобы убедиться, что предыдущий KeyValuePair был успешно восстановлен.)
Но это не будет эффективно.
Если возможно, подумайте об использовании SortedList<TKey, TValue>
(очевидно, это может быть невозможно, если вы не может принимать свои медленные вставки и удаляет). Эта коллекция поддерживает эффективный поиск ключей и значений с помощью упорядоченного индекса, поскольку он реализуется как растущий массив. Тогда ваш запрос станет таким простым, как:
SortedList<int, int> dictionary = ...
int knownKey = ...
int indexOfPrevious = dictionary.IndexOfKey(knownKey) - 1;
// if "known" key exists and isn't the first key
if(indexOfPrevious >= 0)
{
// Wrap these in a KeyValuePair if necessary
int previousKey = dictionary.Keys[indexOfPrevious];
int previousValue = dictionary.Values[indexOfPrevious];
}
IndexOfKey
запускает двоичный поиск в списке ключей, работающий в O(log n)
времени. Все остальное должно работать в постоянное время, то есть вся операция должна выполняться в логарифмическом времени.
В противном случае вам придется реализовать себя/найти коллекцию BST, которая выставляет предшественников/преемников.
Ответ 2
Я также искал ответ на эту проблему, и я подумал, что лучшим решением, чем все ответы здесь, является использование TreeDictionary<K, V>
из C5 Collections (GitHub/NuGet), который представляет собой реализацию красно-черного дерева.
Он имеет методы Predecessor
/TryPredecessor
и WeakPredessor
/TryWeakPredecessor
(а также эквивалентные методы для преемников), который делает именно то, что вы хотите.
Например:
TreeDictionary<int,int> dictionary = new TreeDictionary<int,int>();
dictionary.Add(1,33);
dictionary.Add(2,20);
dictionary.Add(4,35);
// applied to the dictionary itself, returns KeyValuePair<int,int>
var previousValue = dictionary.Predecessor(4);
Assert.Equals(previousValue.Key, 2);
Assert.Equals(previousValue.Value, 20);
// applied to the keys of the dictionary, returns key only
var previousKey = dictionary.Keys.Predecessor(4);
Assert.Equals(previousKey, 2);
// it is also possible to specify keys not in the dictionary
previousKey = dictionary.Keys.Predecessor(3);
Assert.Equals(previousKey, 2);
Ответ 3
KeyValuePair<int, int> lookingForThis = dictionary
.Reverse()
.SkipWhile(kvp => kvp.Key != 4)
.Skip(1)
.FirstOrDefault();
Ответ 4
Dictionary<TKey,TValue>
является несортированным, поэтому для какого-либо элемента нет предыдущей вещи. Вместо этого вы можете использовать SortedDictionary<TKey,TValue>
.
EDIT: Извините, я прочитал только заголовок LOL.
Если вам нужно использовать LINQ:
int givenKey = 4;
var previousItem = dict.Where((pair, index) =>
index == dict.Count ||
dict.ElementAt(index + 1).Key == givenKey)
.FirstOrDefault();
Ответ 5
Вы можете прокручивать словарь и отслеживать значения, я думаю. Что-то вроде этого:
public int GetPreviousKey(int currentKey, SortedDictionary<int, int> dictionary)
{
int previousKey = int.MinValue;
foreach(KeyValuePair<int,int> pair in dictionary)
{
if(pair.Key == currentKey)
{
if(previousKey == int.MinValue)
{
throw new InvalidOperationException("There is no previous key.");
}
return previousKey;
}
else
{
previousKey = pair.Key;
}
}
}
Однако это довольно сложная операция. Тот факт, что вам это нужно, может указывать на проблему с вашим дизайном.
Ответ 6
Я бы предпочел использовать linq, если это случай... попробуйте это, он, безусловно, будет работать
SortedDictionary<int,int> dictionary = new SortedDictionary<int,int>();
dictionary.add(1, 33);
dictionary.add(2, 20);
dictionary.add(4, 35);
int SelectedKey = 4;
var ResutValue = ( from n in dictionary
where n.Key < TheSelectedKey
select n.Value).Last();
this.txtResult.Text = ResultValue.ToString();
Ответ 7
Как насчет этого? Я не проверял его, но должен дать что-то, чтобы начать думать в этом направлении. Надеюсь, что это поможет.
int input = 4;
List<int> lKeys = dictionary.Keys.ToList();
int reqIndex = lKeys.IndexOf(input) - 1;
int reqAnswer = dictionary[reqIndex];
проверить другие условия, например if (reqIndex!= -1) и т.д.