关于层次结构的 C# IComparer

C# IComparer with respect of the hierarchy

我实现了 IComparable / IComparer 用于自定义排序,但我需要保留一些层次结构。

我的 class :

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

测试数据如下:

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

我希望按 ATT_LIBELLE 进行升序/降序排序,但要尊重层次结构。

仅供参考,在 Oracle 下有子句 Order By Siblings。 C#中没有对应的吗?

这是升序所需的结果:

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

这是降序所需的结果:

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

在 C# 中可以吗?

谢谢大家!

编辑:

这是 IComparable 实现的代码:

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;
    }
}

您的数据结构与 IComparable 不兼容。 IComparable 实现成对比较:换句话说,给定任意两个元素,您需要能够知道它们的相对顺序。在您的示例中,这是不可能的,因为您的数据结构是一棵树,比较两个元素需要整个树结构的上下文。

对数据实施排序的挑战在于,您使用的表示是扁平化的关系表示。可能有一种巧妙的就地排序方式,但如果将其转换为内存中的树形表示形式,排序就会变得简单明了:

public class AttributNode
{
    public Attribut       ATTRIBUT        { get; set; }
    public AttributNode[] CHILDREN        { get; set; }

    public void Sort() {
        foreach (Attribut child in CHILDREN) {
            child.Sort();
        }

        Array.Sort(
            CHILDREN,
            (x, y) => x.ATTRIBUT.ATT_LIBELLE.CompareTo(y.ATTRIBUT.ATT_LIBELLE));
    }

    IEnumerator<Attribut> Flatten()
    {
        yield return ATTRIBUT;

        foreach (IEnumerable<Attribut> items in CHILDREN.Select((c) => c.Flatten()))
        {
            foreach (Attribut item in items) {
                yield return item;
            }
        }
    }
}

从展平表示构建树应该包含在其他答案中 like this one

这里是完整的代码,虽然不优雅但是可以工作。在比较器的构造函数中,我创建了一个跟踪 Parent_Libelle 的影子字典。这是为了进行适当的排序。 您可以通过将 Order 设置为 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; }
        }
    }
}