3
\$\begingroup\$

I have this method which successfully sorts the incoming dictionary alphabetically:

private Dictionary<string, string> sortMacroDictionary(Dictionary<string, string> whichDictionary) {
    Dictionary<string, string> sortedDictionary = new Dictionary<string, string>();
    List<string> tempList = new List<string>();
    string outValue;

    if (whichDictionary.Count > 0) {
        foreach (var entry in whichDictionary) {
            tempList.Add(entry.Key);
        }

        tempList.Sort();

        foreach (string entry in tempList) {
            if (whichDictionary.TryGetValue(entry, out outValue)) {
                sortedDictionary.Add(entry, outValue);
            }
        }
    }

    return sortedDictionary;
}

It works as intended and I am not experiencing any issues with it. I'm just wondering if there is a way to improve this method? Maybe there are some issues with my current code that I haven't foreseen?

\$\endgroup\$
15
\$\begingroup\$

I'm afraid your sorting is in vain because the normal dictionary does not guarantee that the items will be enumerated in the same order as you added them:

For purposes of enumeration, each item in the dictionary is treated as a KeyValuePair<TKey, TValue> structure representing a value and its key. The order in which the items are returned is undefined.

If they are, then this is a pure coincidence.

You'll be safe if you use the SortedDictionary instead which:

Represents a collection of key/value pairs that are sorted on the key.


If we assume for a moment that a Dictionary<TKey, TValue> can be sorted the way you do it then it can be also achieved with a simple LINQ query:

private Dictionary<string, string> sortMacroDictionary(Dictionary<string, string> source)
{
    return source.Keys.OrderBy(k => k).ToDictionary(k => k, k => source[k]);    
}

You first get all keys, order them, then you create a new dictionary based on the new key order. This is virtually the same as what you are doing now but much shorter.

As far as your code is concerned there is no need to check whichDictionary.Count > 0 becasue if the dictionary is empty your loops won't run. The same applies to this query.

My advice is to use the SortedDictionary. This dictionary maintains the order the entire time. It sorts itself so to speak, as you add/remove items. You don't have to sort it manually.

\$\endgroup\$
1
\$\begingroup\$

You can load the List like this

List<string> tempList = new List<string>(whichDictionary.Keys);

That is Java style { }. C# is typically on new line.

Dictionary does not guarantee order but you could use OrderedDictionary. Turns out OrderedDictionary does not guarantee order either. I thought might be able to just use the index but it appears you cannot retrieve the key from the index.

Shorter:

Dictionary<string, string> dic = new Dictionary<string, string> 
                                 { { "csync", "a" },  { "bsync", "a" } };
OrderedDictionary oDic = new OrderedDictionary(dic.Count);
foreach (KeyValuePair<string, string> kvp in dic.OrderBy(x => x.Key))
{
    oDic.Add(kvp.Key, kvp.Value);
}

Or SortedDictionary as given in another answer.

\$\endgroup\$
  • \$\begingroup\$ The OrderedDictionary won't work. It's for retrieving items by index, it does not guarantee order either: The elements of an OrderedDictionary are not sorted by the key. \$\endgroup\$ – t3chb0t Aug 20 '17 at 17:22
  • \$\begingroup\$ @t3chb0t Well shoot. In a small test was correct for me. \$\endgroup\$ – paparazzo Aug 20 '17 at 17:33
0
\$\begingroup\$

A small improvement would be to make it a extension method on Dictionary, this will guarantee that whichDictionary is not NULL

edit; well guarantee is maybe a bit big, but a little bigger maybe. In this perspective you do also need to check that whichDictionary is not NULL

\$\endgroup\$
0
\$\begingroup\$

EDIT: As Paparazzi made it clear to me, a Dictionary is not guaranteed to hold order. I did some research into why and edited my answer to accommodate this fact.


A Dictionary is an implementation of a hash table. When we look at what Add(TKey, TValue) actually does, it assigns each key to a bucket which is then stored in an array of buckets.

Each time you resize your Dictionary (removing, inserting), it will be rehashed and it is not guaranteed to be the same order each time.

Refer to this for better understanding of how Dictionary is implemented.

If you care about keeping the order of your Dictionary, you can create a SortedDictionary which will preserve order. SortedDictionary can take a Dictionary as a parameter as one of its overloaded constructors:

var sortedDictionary = new SortedDictionary<string, string>(dictionary);

If you do want a method of enumerating by key value (alphabetically, in this case), you can use LINQ. It's only slightly different than t3chb0t's answer but I thought I'd add my way:

Dictionary<string, string> sortMacroDictionary(Dictionary<string, string> whichDictionary)
{
    return whichDictionary.OrderBy(x => x.Key).ToDictionary(x => x.Key, y => y.Value);
}

This creates an IOrderedEnumerable<KeyValuePair<string, string>>. We then use Enumerable.ToDictionary to generate a dictionary based on that order.

You can also use a sorted list (using an IComparer) to achieve the same result:

Dictionary<string, string> sortMacroDictionary(Dictionary<string, string> whichDictionary)
{
    var sortedList = whichDictionary.ToList();
    sortedList.Sort((x, y) => x.Key.CompareTo(y.Key));
    return sortedList.ToDictionary(x => x.Key, y => y.Value);
}
\$\endgroup\$
  • \$\begingroup\$ It is not guaranteed to hold order. \$\endgroup\$ – paparazzo Aug 20 '17 at 22:03
  • \$\begingroup\$ That is correct, but it will take a dictionary and return the sorted version of it. I mean, you could sort it every time you add entries to your dictionary -- but you're probably better off just using a SortedDictionary than calling a method every time you want to make changes -- when that's essentially what a SortedDictionary will do when you add by default. Creating a SortedDictionary using an unsorted dictionary, and not converting it back (like I did in my answer), will hold order. \$\endgroup\$ – Tristan Gibson Aug 20 '17 at 22:06
  • \$\begingroup\$ That Dictionary is not guaranteed to be sorted even if you sort every time. \$\endgroup\$ – paparazzo Aug 20 '17 at 22:25
  • \$\begingroup\$ @Paparazzi I viewed the overload constructor for Dictionary and you are correct; it uses its own Add() method. This makes sense, so I suppose SortedDictionary is going to be my answer's merit until I come up with something better. \$\endgroup\$ – Tristan Gibson Aug 20 '17 at 23:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.