3

My hashtable contains (key, Values[])

e.g:

myHashtable[keys, Values[]]

myHashtable.Add[1, Value1];
myHashtable.Add[2, Value2];
myHashtable.Add[3, Value3]; 
myHashtable.Add[4, Value4];
myHashtable.Add[5, Value5];

Where Value1; Value2, value3, value4, and value5 is as follows.

Value1[name = "Smith"]
Value1[Title= "Mr"]
Value1[Salary = 1000]
Value1[Identity = "S"]

Value2[name = "Peter"]
Value2[Title= "Mr"]
Value2[Salary = 1000]
Value2[Identity = "A"]


Value3[name = "Tom"]
Value3[Title= "Mr"]
Value3[Salary = 1000]
Value3[Identity = "C"]


Value4[name = "Marry"]
Value4[Title= "Ms"]
Value4[Salary = 1000]
Value4[Identity = ""]

Value5[name = "Sam"]
Value5[Title= "Mr"]
Value5[Salary = 1000]
Value5[Identity = "C"]

I want to order this dictionaryEntry list values where Identity with "C" values first then "A" then "S" then ""

After sorting the Result should be like as follows.

myHashtable.Add[3, Value3]; // Value3.Identity = "C"
myHashtable.Add[5, Value5]; // Value5.Identity = "C"
myHashtable.Add[2, Value2]; // Value2.Identity = "A"
myHashtable.Add[1, Value1]; // Value1.Identity = "S"
myHashtable.Add[4, Value4]; // Value4.Identity = ""

Here is my attempt.

var result1 = new List<DictionaryEntry>(hashtable.Count);
var result2 = new List<DictionaryEntry>(hashtable.Count);
var result3 = new List<DictionaryEntry>(hashtable.Count);                 
var result4 = new List<DictionaryEntry>(hashtable.Count);


var result = new List<DictionaryEntry>(hashtable.Count);

foreach (DictionaryEntry entry in hashtable)
   {
        result.Add(entry);
   }


                foreach (DictionaryEntry dictionaryEntry in result)
                {
                    var t2 = dictionaryEntry.Value;

                    switch (t2.Identity)
                    {
                        case "C":
                            result1.Add(dictionaryEntry);
                            break;
                        case "A":
                            result2.Add(dictionaryEntry);
                            break;
                        case "S":
                            result3.Add(dictionaryEntry);
                            break;
                        case "":
                            result4.Add(dictionaryEntry);
                            break;
                        default:
                            break;
                    }
                }
                result1.ToList();
                result2.ToList();
                result3.ToList();


                var combinedResult = result1.Union(result2)
                    .Union(result3)
                    .Union(result4)
                    .ToDictionary(k => k.Key, v => v.Value).OrderByDescending(v => v.Value);

How can I sort combinedResult to give me the above custom sorted dictionary entry list?

Any help is greatly appriciated. Thank You

MJA
  • 35
  • 1
  • 5
  • Your `case` does (effectively) nothing, because after it gets turned into a dictionary the ordering disappears. I'd probably supply an instance of [SortedDictionary](https://msdn.microsoft.com/en-us/library/f7fta44c(v=vs.110).aspx) with a custom comparator if I needed the ordering maintained. If you just want the output of the enumeration ordered, do that in the `OrderByDescending`. – Clockwork-Muse Apr 18 '16 at 04:34

1 Answers1

6

When a Dictionary data structure is implemented with a hashtable, in order to achieve the amortized O(1) insert/delete/update operations the data is unsorted. On the other hand, when the Dictionary is implemented with a balanced tree, the operations are a bit slower O(logn) but they can be enumerated in sorted way (by the key). For example, C# dictionary implementation is unsorted, and a C++ map is sorted (based on a red-black tree)
Given the above (you cannot have the data sorted however you want in the dictionary), What you can do is save the dictionary as a List/Array, and then sort by whichever comparator you want.

Here is an example of a Dictionary and a custom comparer, where you can get the values in the dictionary sorted by the logic in the custom comparer:

public class Data
{
    public string Name { get; set; }
    public string Identity { get; set; }
}

public class CustomerComparer : IComparer<KeyValuePair<int, Data>>
{
    private List<string> orderedLetters = new List<string>() { "C", "A", "S" };

    public int Compare(KeyValuePair<int, Data> str1, KeyValuePair<int, Data> str2)
    {
        return orderedLetters.IndexOf(str1.Value.Identity) - orderedLetters.IndexOf(str2.Value.Identity);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Data value1 = new Data { Name = "Name1", Identity = "S" };
        Data value2 = new Data { Name = "Name2", Identity = "A" };
        Data value3 = new Data { Name = "Name3", Identity = "C" };
        Data value4 = new Data { Name = "Name4", Identity = "C" };

        Dictionary<int, Data> unsortedDictionary = new Dictionary<int, Data>();
        unsortedDictionary.Add(1, value1);
        unsortedDictionary.Add(2, value2);
        unsortedDictionary.Add(3, value3);
        unsortedDictionary.Add(4, value4);

        var customSortedValues = unsortedDictionary.Values.OrderBy(item => item, new CustomerComparer()).ToArray();

        for (int i=0; i < customSortedValues.Length; i++)
        {
            var kvp = customSortedValues[i];
            Console.WriteLine("{0}: {1}=(Name={2}, Identity={3})", i, kvp.Key, kvp.Value.Name, kvp.Value.Identity);
        }
    }
}
//Output is:  
//0: Name3=C
//1: Name4=C
//2: Name2=A
//3: Name1=S

You can also use a SortedDictionary (as @Clockwork-Muse suggests) and pass a similar CustomComparer as in the above example. This really depends on what your requirements are. If you need the operations to stay fast and only need the values sorted for, perhaps, reporting, then just sort when the values are needed (as in my example). If you will be accessing the sorted values alot, it may make sense to just keep them sorted in the first place.

Itsik
  • 3,920
  • 1
  • 25
  • 37
  • Thank you Itsik! How would you insert the input to Dictionary unsortedDictionary if the input comes from a Hashtable with a key & values? Where Dictionary in the example above? – MJA Apr 18 '16 at 07:37
  • DictionaryEntry contains an `object` for both Key and Value, so you could just cast them `dictionary.Add((int)dictionaryEntry.Key, (Data)dictionaryEntry.Value)`. Is there are specific reason you are using a Hashtable to begin with? And not the generic Dictionary? – Itsik Apr 18 '16 at 16:26
  • Not sure why the program used Hashtable yet. But here is how I am trying to insert the input(hashtable result) to unsortedDictionary. However, I could not get dictionary "Key" value into unsortedDictionary. `[Dictionary unsortedDictionary = new Dictionary(); foreach (DictionaryEntry entry in HashtableResult) { unsortedDictionary.Add((int)entry.Key, (Data)entry.Value); } var customSortedValues = unsortedDictionary.Values.OrderBy(item => item, new CustomerComparer()).ToArray();]` – MJA Apr 18 '16 at 19:18
  • So with the above input the value of unsortedDictrionary would become as follows. `[unsortedDictionary.Add(entry.key, (Data)entry.Value) would have a collection of unsortedDictionary[0]: {[Key1, Data1]} ; unsortedDictionary[1]: {[key2, Data2]} ]` and so forth. Once sorted I am lossing the key values and it would just become a list of Data. – MJA Apr 18 '16 at 20:37
  • Thats because in my example I just sorted the Values. I updated the example to keep the KeyValuePairs. – Itsik Apr 19 '16 at 00:09
  • Here is what I did to keep the "Key" from Hashtable after applying the custom sorting. Basically, replacing ToArray() by ToDictionary() by the following code. `[var customSortedValues = unsortedDictionary.Values.OrderBy(item => item, new CustomerComparer()).ToDictionary(item => item.key, item => item.Value);`] Thank you for your answer! – MJA Apr 21 '16 at 17:24
  • If you change back to dictionary you are losing the order... I was under the impression that is not what you wanted – Itsik Apr 21 '16 at 18:05