【问题标题】:How do I get previous key from SortedDictionary?如何从 SortedDictionary 获取上一个键?
【发布时间】:2011-06-10 21:04:05
【问题描述】:

我有包含键值对的字典。

SortedDictionary<int,int> dictionary=new SortedDictionary<int,int>();
dictionary.Add(1,33);
dictionary.Add(2,20);
dictionary.Add(4,35);

我想从一个已知的键值中获取以前的键值对。在上述情况下,如果我有密钥 4,那么我如何获得&lt;2,20&gt;

【问题讨论】:

  • SortedDictionary dictionary=new SortedDictionary();
  • 我认为this question 的答案解释了如何做你想做的事。
  • 是否有任何 Linq 查询可以给我以前的密钥。
  • user578083,我已经在下面使用 linq 查询给出了答案,检查一下...好的 :)

标签: c# .net linq sorteddictionary


【解决方案1】:

Dictionary&lt;TKey,TValue&gt; 未排序,因此某些项目没有 previous 内容。相反,您可以使用SortedDictionary&lt;TKey,TValue&gt;

编辑:对不起,我只看了标题哈哈。

如果您必须使用 LINQ:

int givenKey = 4;
var previousItem = dict.Where((pair, index) => 
                                   index == dict.Count || 
                                   dict.ElementAt(index + 1).Key == givenKey)
                       .FirstOrDefault();

【讨论】:

  • 尝试在接受 dictgivenKey 作为我的字典和我开始的键的函数中使用它,并将 previousItem 作为字符串返回,因为我有一个字典&lt;string, DateTime&gt;。我收到一个错误:Cannot implicitly convert type 'System.Collections.Generic.KeyValuePair&lt;string, System.DateTime&gt;' to 'string'。所以这不仅仅是返回密钥 - 我确认它返回整个 KeyValuePair。所以在我的情况下,我做了KeyValuePair&lt;string, DateTime&gt; tempKVP = MoveToPreviousKey(myDict, givenKey);,然后是string previousKey = tempKVP.Key;。工作得很好。 HTH 某人。
【解决方案2】:

我猜你可以遍历字典并跟踪值。像这样的:

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

但是,这是一个非常奇怪的操作。您需要它的事实可能表明您的设计存在问题。

【讨论】:

    【解决方案3】:
    KeyValuePair<int, int> lookingForThis = dictionary
      .Reverse()
      .SkipWhile(kvp => kvp.Key != 4)
      .Skip(1)
      .FirstOrDefault();
    

    【讨论】:

    • +1。只想指出,OP的问题并没有真正的意义。虽然此答案将返回它们之前的配对,但当您将项目添加到字典时,之前的配对将不断变化。我认为需要进一步澄清 OP 试图做什么
    • 我觉得应该是.SkipWhile(kvp =&gt; kvp.Key != 4).Skip(1)
    【解决方案4】:

    使用SortedDictionary&lt;TKey, TValue&gt; 很难有效地实现这一点,因为它是作为不暴露前任或后继的二叉搜索树实现的。

    您当然可以枚举每个 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&lt;TKey, TValue&gt; 代替(显然,如果您不能采用较慢的插入和删除,这可能是不可能的)。该集合支持通过 ordered 索引进行高效的键和值检索,因为它是作为可增长数组实现的。然后您的查询就变得如此简单:

    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 集合。

    【讨论】:

      【解决方案5】:

      如果是这种情况,我更愿意使用 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();
      

      【讨论】:

        【解决方案6】:

        这个怎么样?虽然我还没有测试过,但应该给一些东西开始朝这个方向思考。希望对您有所帮助。

                int input = 4;
                List<int> lKeys = dictionary.Keys.ToList();
                int reqIndex = lKeys.IndexOf(input) - 1;
                int reqAnswer = dictionary[reqIndex];
        

        测试其他条件,例如 if (reqIndex != -1) 等。

        【讨论】:

          【解决方案7】:

          我也在寻找这个问题的答案,我认为比这里所有答案更好的解决方案是使用C5 CollectionsGitHub/NuGet)中的TreeDictionary&lt;K, V&gt;,即红黑树的实现。

          它有 Predecessor/TryPredecessorWeakPredessor/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);
          

          【讨论】:

          • 如果有人不知道弱前任是什么,它是第一项等于或小于传递的值。 (严格的)前任是刚刚小于传递值的项目。继任者也是如此。
          • C5 集合很酷,但它们在最新版本的文档中存在问题,因此请节省您的时间,克隆源代码并查看源代码。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-07-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-14
          • 1970-01-01
          • 2022-09-23
          相关资源
          最近更新 更多