How can I sort (Custom Sort) list of Dictionary en

2019-05-10 23:45发布

问题:

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

回答1:

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.