Search code examples
c#hierarchical-dataicomparableicomparer

C# IComparer with respect of the hierarchy


I implement IComparable / IComparer for custom sorting but I need to keep some hierarchy.

My class :

public class Attribut : IComparable
{
    public int       ATT_ID          { get; set; }
    public string    ATT_LIBELLE     { get; set; }
    public int       ATT_PARENT_ID   { get; set; }
}

The test data are as follows:

ATT_ID      ATT_LIBELLE                         ATT_PARENT_ID   
356         Avis client requis                  0
357         Nom du destinataire client          356
358         Date d'envoi au client pour avis    356
366         CNPE ?                              0
367         Palier                              366
368         Tranche                             366
369         Site                                366 
370         Materiel                            366
371         Machine                             366
372         Affaire parent                      366

I would like sorting is done ascending / descending on ATT_LIBELLE but respecting the hierarchy.

FYI, under Oracle there is the clause Order By Siblings. There is no equivalent in C #?

Here is the desired result for ascending:

ATT_ID      ATT_LIBELLE                         ATT_PARENT_ID   
356         Avis client requis                  0
358         Date d'envoi au client pour avis    356
357         Nom du destinataire client          356
366         CNPE ?                              0
372         Affaire parent                      366
371         Machine                             366
370         Materiel                            366
367         Palier                              366
369         Site                                366
368         Tranche                             366

Here is the desired result for descending:

ATT_ID      ATT_LIBELLE                         ATT_PARENT_ID   
366         CNPE ?                              0
368         Tranche                             366
369         Site                                366
367         Palier                              366
370         Materiel                            366
371         Machine                             366
372         Affaire parent                      366
356         Avis client requis                  0
357         Nom du destinataire client          356
358         Date d'envoi au client pour avis    356

It's possible in C# ?

Thanks all!

EDIT :

Here is the code of the IComparable implementation :

public static IComparer sortAscending_ATT_LIBELLE       { get { return new sortLibelleAscendingHelper(); } }
public static IComparer sortDescending_ATT_LIBELLE      { get { return new sortLibelleDescendingHelper(); } }

private class sortLibelleAscendingHelper : IComparer
{
    int IComparer.Compare(object a, object b)
    {
        var oAtta = (a as Attribut);
        var oAttb = (b as Attribut);

        if (a == null || b == null) { return 0; }

        int ret = (oAtta.ATT_LIBELLE).CompareTo(oAttb.ATT_LIBELLE);

        if ((oAtta.ATT_PARENT_ID != oAttb.ATT_PARENT_ID) || (oAtta.ATT_PARENT_ID == oAttb.ATT_ID))
        {
            ret = 1;
        }

        return ret;
    }
}

private class sortLibelleDescendingHelper : IComparer
{
    int IComparer.Compare(object a, object b)
    {
        var oAtta = (a as Attribut);
        var oAttb = (b as Attribut);

        if (a == null || b == null) { return 0; }

        int ret = (oAttb.ATT_LIBELLE).CompareTo(oAtta.ATT_LIBELLE);

        if ((oAtta.ATT_PARENT_ID != oAttb.ATT_PARENT_ID) || (oAtta.ATT_PARENT_ID == oAttb.ATT_ID))
        {
            ret = -1;
        }

        return ret;
    }
}

Solution

  • Here the complete code, it is not elegant but working. On the constructor of the comparer I create a shadowdictionary keeping track of Parent_Libelle. This is to do proper sorting. You can do ascending sorting by setting Order to true.

    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Linq;
    
    namespace ConsoleApp1
    {
        class Program
        {
    
            static void Main(string[] args)
            {
                List<Attribut> sortList = new List<Attribut>();
                sortList.Add(new Attribut() { ATT_ID = 356, ATT_LIBELLE = "Avis client requis", ATT_PARENT_ID = 0 });
                sortList.Add(new Attribut() { ATT_ID = 357, ATT_LIBELLE = "Nom du destinataire client", ATT_PARENT_ID = 356 });
                sortList.Add(new Attribut() { ATT_ID = 358, ATT_LIBELLE = "Date d'envoi au client pour avis", ATT_PARENT_ID = 356 });
                sortList.Add(new Attribut() { ATT_ID = 366, ATT_LIBELLE = "CNPE ?", ATT_PARENT_ID = 0 });
                sortList.Add(new Attribut() { ATT_ID = 367, ATT_LIBELLE = "Palier", ATT_PARENT_ID = 366 });
                sortList.Add(new Attribut() { ATT_ID = 368, ATT_LIBELLE = "Tranche", ATT_PARENT_ID = 366 });
                sortList.Add(new Attribut() { ATT_ID = 369, ATT_LIBELLE = "Site", ATT_PARENT_ID = 367 });
                sortList.Add(new Attribut() { ATT_ID = 370, ATT_LIBELLE = "Materiel", ATT_PARENT_ID = 367 });
                sortList.Add(new Attribut() { ATT_ID = 371, ATT_LIBELLE = "Machine", ATT_PARENT_ID = 366 });
                sortList.Add(new Attribut() { ATT_ID = 372, ATT_LIBELLE = "Affaire parent", ATT_PARENT_ID = 366 });
    
    
    
                Random rand = new Random();
                for (int i = 0; i < 30; i++)
                {
                    int ra = rand.Next(10);
                    Attribut move = sortList[ra];
                    sortList.RemoveAt(ra);
                    sortList.Add(move);
                }
    
                sortList.Sort(new CompareAttribut(sortList, false));
    
                foreach (Attribut oneAtt in sortList)
                {
                    Console.WriteLine(oneAtt.ATT_ID + " " + oneAtt.ATT_LIBELLE + " " + oneAtt.ATT_PARENT_ID);
                }
    
            }
    
            public class CompareAttribut : IComparer<Attribut>
            {
                private class AttributTree
                {
                    private Attribut self;
                    public AttributTree(Attribut Self)
                    {
                        self = Self;
                    }
                    public Attribut Self
                    {
                        get { return self; }
                    }
                    public AttributTree Parent { get; set; }
    
                    public string [] SortorderLib { get; set; }
    
                }
    
                private bool order = false;
    
                private Dictionary<int,AttributTree> kHelpers = new Dictionary<int, AttributTree>();
                public CompareAttribut(List<Attribut> StartList, bool Order)
                {
    
                    order = Order;
    
                    foreach (Attribut a in StartList)
                    {
                        int key = a.ATT_ID;
                        AttributTree at = new AttributTree(a);
    
    
                        //string value = a.ATT_PARENT_ID > 0 ? StartList.Single(p => p.ATT_ID == a.ATT_PARENT_ID).ATT_LIBELLE : a.ATT_LIBELLE;
    
                        kHelpers.Add(key, at);
                    }
    
                    //Create the tree
                    foreach (AttributTree at in kHelpers.Values)
                    {
                        at.Parent = kHelpers[at.Self.ATT_ID];
                    }
    
                    foreach (AttributTree at in kHelpers.Values)
                    {
                        List<string> libelles = new List<string>();
                        libelles.Add(at.Self.ATT_LIBELLE);
                        AttributTree up = at;
    
                        while (up.Self.ATT_PARENT_ID != 0)
                        {
                            up = kHelpers[up.Self.ATT_PARENT_ID];
                            libelles.Insert(0, up.Self.ATT_LIBELLE);
                        }
    
                        at.SortorderLib = libelles.ToArray();
                    }
    
    
    
                }
    
                public int Compare(Attribut x, Attribut y)
                {
                    string[] xParentLib = kHelpers[x.ATT_ID].SortorderLib;
                    string[] yParentLib = kHelpers[y.ATT_ID].SortorderLib;
    
                    int i = 0;
    
                    int outcome = 0;
                    while (outcome == 0)
                    {
                        if (i == xParentLib.Length) outcome = -1;//x above y
                        else if (i == yParentLib.Length) outcome = 1;//x  under y
                        else outcome = xParentLib[i].CompareTo(yParentLib[i]);
                        if (outcome == 0)
                        {
                            i++;
                            continue;
                        }
                        break;
                    }
                    return outcome * (order ? 1 : -1); 
                }
            }
    
            public class Attribut
            {
                public int ATT_ID { get; set; }
                public string ATT_LIBELLE { get; set; }
                public int ATT_PARENT_ID { get; set; }
            }
        }
    }