简体   繁体   中英

Sorting a nested dictionaries using C#

I have a dictionary object with the type like the below.

Dictionary<string, Dictionary<Roles, Dictionary<Period, List<Product>>>>

The Roles (is an enum) that has "Preparers" & "Approvers" as their items. Similarly Period is another enum that has "Ahead" & "Past" items.

The List holds list of various products.

I have items in the following hierarchical structure in the dictionary.

"Sachin" --> Roles.Preparer --> Period.Past --> Products
"Sachin" --> Roles.Approver --> Period.Ahead --> Products
"Sachin" --> Roles.Approver --> Period.Ahead --> Products
"Sachin" --> Roles.Approver --> Period.Past --> Products

I will have to sort the dictionary in the following order.

"Sachin" --> Roles.Preparer --> Period.Ahead --> Products
"Sachin" --> Roles.Approver --> Period.Ahead --> Products
"Sachin" --> Roles.Preparer --> Period.Past --> Products
"Sachin" --> Roles.Approver --> Period.Past --> Products

This structure is required because I will have to iterate through every item and should add as part of the mail.

The actual code is like this.

`using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Basics
{
    class Product
    {
        public string Name { get; set; }
        public int Days { get; set; }

    }

    enum Period
    {
        Ahead,
        Past
    }

    enum Roles
    {
        Preparer,
        Approver
    }

    class Program
    {
        static void Main(string[] args)
        {
            DictionaryProcessing(new string[] { "sriram123@yahoo.com", "abhishek321@yahoo.com" });
        }

        private static void DictionaryProcessing(string[] emailIDs)
        {
            List<Product> products = new List<Product>();

            Product product1 = new Product() { Name = "Pencil", Days = 14 };
            Product product2 = new Product() { Name = "Eraser", Days = 2 };
            Product product3 = new Product() { Name = "Geometry Box", Days = 31 };

            products.Add(product1);
            products.Add(product2);
            products.Add(product3);

            Dictionary<string, Dictionary<Roles, Dictionary<Period, List<Product>>>> dict = new Dictionary<string, Dictionary<Roles, Dictionary<Period, List<Product>>>>();

            ///

            foreach (string emailID in emailIDs)
            {

                if (!dict.ContainsKey(emailID))
                    dict.Add(emailID, new Dictionary<Roles, Dictionary<Period, List<Product>>>());

                if (!dict[emailID].ContainsKey(Roles.Preparer))
                    dict[emailID].Add(Roles.Preparer, new Dictionary<Period, List<Product>>());

                if (!dict[emailID][Roles.Preparer].ContainsKey(Period.Ahead))
                    dict[emailID][Roles.Preparer].Add(Period.Ahead, new List<Product>());

                if (!dict[emailID][Roles.Preparer].ContainsKey(Period.Past))
                    dict[emailID][Roles.Preparer].Add(Period.Past, new List<Product>());

                ///

                if (!dict[emailID].ContainsKey(Roles.Approver))
                    dict[emailID].Add(Roles.Approver, new Dictionary<Period, List<Product>>());

                if (!dict[emailID][Roles.Approver].ContainsKey(Period.Ahead))
                    dict[emailID][Roles.Approver].Add(Period.Ahead, new List<Product>());

                if (!dict[emailID][Roles.Approver].ContainsKey(Period.Past))
                    dict[emailID][Roles.Approver].Add(Period.Past, new List<Product>());

                for (int i = 0; i < products.Count; i++)
                {
                    dict[emailID][Roles.Preparer][Period.Ahead].Add(products[i]);
                    dict[emailID][Roles.Preparer][Period.Past].Add(products[i]);
                    dict[emailID][Roles.Approver][Period.Past].Add(products[i]);
                    dict[emailID][Roles.Approver][Period.Ahead].Add(products[i]);
                }


            }
        }
    }
}
`

How can I sort it in this order? I am limited to use .NET 2.0 framework.

Dictionaries can not be sorted. They are not lists. Additionally:

Your structure is bad - a Dictionary can not contain the same key more than once, so the samples you are providing are not even possible to create:

"Sachin" --> Roles.Preparer --> Period.Past --> Products
"Sachin" --> Roles.Approver --> Period.Ahead --> Products
"Sachin" --> Roles.Approver --> Period.Ahead --> Products
"Sachin" --> Roles.Approver --> Period.Past --> Products

The "outer" dictionary can not contain the key "Sachin" more than once. The "inner dictionary" can not contain the role Approver more than once and also on the last level, Period.Past/Ahead can not be key more than once.

Change your structure to List<T> instead, where T is an appropriate data structure, or, as other have already noted, change to typed datasets now to treat your structure like a table.

EDIT
I'm editing my answer now, just to make sure that we all understand what everyone is talking about.

I'm saying, it is not possible for a dictionary to have the same key twice. So, according to this rule, your cases must be reduced to the following:

"Sachin" --> Roles.Preparer --> Period.Past --> Products
"Sachin" --> Roles.Approver --> Period.Ahead --> Products
"Sachin" --> Roles.Approver --> Period.Past --> Products

Now that we're talking about something that applies to the rules, we can ask "how to sort?". The answer: you can't. A dictionary is by definition an unordered structure. You can, however, make sure to retrieve the values in a specific order. If you want to "sort" the products, so that the Past products always come before the Ahead products, just make sure to use the respective key first.

EDIT 2

Just realized this is based on a copy/paste error. The data you're talking about should read:

"Sachin" --> Roles.Preparer --> Period.Ahead --> Products
"Sachin" --> Roles.Preparer --> Period.Past --> Products
"Sachin" --> Roles.Approver --> Period.Past --> Products
"Sachin" --> Roles.Approver --> Period.Ahead --> Products

You said you're using this code to add the items:

for (int i = 0; i < products.Count; i++)
{
    dict[emailID][Roles.Preparer][Period.Ahead].Add(products[i]);
    dict[emailID][Roles.Preparer][Period.Past].Add(products[i]);
    dict[emailID][Roles.Approver][Period.Past].Add(products[i]);
    dict[emailID][Roles.Approver][Period.Ahead].Add(products[i]);
}

Then you can use similar code to retrieve the items. Given an eMail-ID, the following would get you a list of items in the ordered by "preparer before approver" and "past before ahead":

List<Product> productsForEMailID = new List<Product>();

productsForEMailID.AddRange(dict[emailID][Roles.Preparer][Period.Past]);
productsForEMailID.AddRange(dict[emailID][Roles.Approver][Period.Past]);
productsForEMailID.AddRange(dict[emailID][Roles.Preparer][Period.Ahead]);
productsForEMailID.AddRange(dict[emailID][Roles.Approver][Period.Ahead]);

The list of products is "sorted".

You could use another type of key, more suited for this problem. If you want a quick hack for a SortedDictionary<string, List<Product>> :

"Sachin#0#0" --> Products
"Sachin#0#1" --> Products
...

Here I assume that the '#' character cannot occur in a name. The first number represents Roles as Preparer = 0 , Approver = 1 , and the second number represents Period as Ahead = 0 , Past = 1 .

Or - if you need a slightly more robust solution, I would make something like:

public struct Key : IComparable<Key>
{
    public String Name;
    public Roles Role;
    public Period Period;

    public int CompareTo(Key other)
    {
        var c = String.Compare(Name, other.Name, StringComparison.Ordinal);
        if (c != 0) return c;
        c = Role.CompareTo(other.Role);
        if (c != 0) return c;
        return Period.CompareTo(other.Period);
    }
}

... and use a SortedDictionary<Key, List<Product>> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM