Search code examples
c#sorteddictionary

SortedDictionary with class for a key becomes unsorted


SortedDictionary is supposed to always be sorted by its key.

Now consider the following code:

public class ContentKey
{
    public int key_num = 0;
}

public static void Main(string[] args)
{
    ContentKey a = new ContentKey() {key_num = 10};
    ContentKey b = new ContentKey() {key_num = 20};
    ContentKey c = new ContentKey() {key_num = 30};
    
    SortedDictionary<ContentKey, string> dict = new SortedDictionary<ContentKey, string>(new KeyComparer());
    dict.Add(a, "A");
    dict.Add(c, "C");
    dict.Add(b, "B");
    
    foreach(var key_value in dict)
    {
        Console.WriteLine("Key val: " + key_value.Key.key_num + " Str: " + key_value.Value);
    }

    a.key_num = 50;
    
    foreach(var key_value in dict)
    {
        Console.WriteLine("Key val: " + key_value.Key.key_num + " Str: " + key_value.Value);
    }
}
                                     
public class KeyComparer : IComparer<ContentKey>
{
    public int Compare(ContentKey a, ContentKey b)
    {
        return a.key_num.CompareTo(b.key_num);
    }
}

https://dotnetfiddle.net/8wwK8P

I would expect it to print: A, B, C for the first batch and then B, C, A. However it's A,B,C in both cases. This means the dictionary has not "realized" that the state of the key has in fact changed.

How can this be solved (without removing and reinserting A, since in practice I may not know which of many keys has changed)?


Solution

  • In my opinion you're going about things wrong here. A list or an array would be more appropriate but here's the solution to your problem. You can remove the stuff related to the Boolean value if you'd like, that's only there for your benefit of tracking the values.

    public class ContentKey
    {
        private int _key_num = 0;
        public bool keyChanged = false;
    
        public ContentKey(int keynum = 0)
        {
          _key_num = keynum;
        }
        
        public int key_num
        {
            get { return _key_num; }
            set
            {
                if (_key_num != value)
                {
                    _key_num = value;
                    keyChanged = true;
                }
            }
        }
    }
    
    
    public static void Main(string[] args)
    {
        ContentKey a = new ContentKey(10);
        ContentKey b = new ContentKey(20);
        ContentKey c = new ContentKey(30);
    
        SortedDictionary<ContentKey, string> dict = new SortedDictionary<ContentKey, string>(new KeyComparer());
        dict.Add(a, "A");
        dict.Add(c, "C");
        dict.Add(b, "B");
    
        foreach (var key_value in dict)
        {
            Console.WriteLine("Key val: " + key_value.Key.key_num + " Key Changed: " + key_value.Key.keyChanged + " Str: " + key_value.Value);
        }
    
        a.key_num = 50;
        ReSortDict(ref dict);
    
        foreach (var key_value in dict)
        {
            Console.WriteLine("Key val: " + key_value.Key.key_num + " Key Changed: " + key_value.Key.keyChanged + " Str: " + key_value.Value);
        }
    }
    
    public class KeyComparer : IComparer<ContentKey>
    {
        public int Compare(ContentKey a, ContentKey b)
        {
            return a.key_num.CompareTo(b.key_num);
        }
    }
    
    public static void ReSortDict(ref SortedDictionary<ContentKey, string> dict)
    {
      SortedDictionary<ContentKey, string> tempDict = new SortedDictionary<ContentKey, string>(new KeyComparer());
      foreach(var key_value in dict)
      {
        tempDict.Add(key_value.Key,key_value.Value);
      }
      dict = tempDict;
    }
    

    What this code does is it reads in the dictionary by reference and adds them to a sorted dictionary same as you did earlier in your code, it then overrides the dictionary object with the new values sorted.