如何根据给定的设置对对象列表进行多分类?

时间:2019-06-08 05:27:37

标签: c# sorting

目标:您有一个对象列表,希望最终用户能够选择一些对象属性来定义排序并为每个属性选择排序方向。

让我们以这个Node对象为例:

    public class Node
    {
        public int CustomProperty { get; set; }
        public string CustomStringProperty { get; set; }

        public string id;
        public string parentId;
        public string Speed;
        public int WheelNumber;
        public int SeatNumber;

        public List<Node> Child { get; set; }

        public Node()
        {
            Child = new List<Node>();
        }
    }

假设您有一个要根据速度,轮数或座位号等条件排序的节点列表。

您将根据目标使用任何.OrderBy,.ThenByDescending等,但是问题在于它很难进行编码,因此开发人员目前正在管理代码;最终用户将不必深入研究代码,甚至每次需要更改标准集合以进行排序并为每个标准(对象属性)定义方向时,情况都将更少。

您可以使用一些代码来帮助用户定义首选项,例如:

public static List<Node> Sort(List<Node> nodes, int level, string[] sortingPreferences)
    {
        // Recursively sort nodes children
        foreach (var node in nodes)
            node.Child = Sort(node.Child, level + 1, sortingPreferences);

           // Now sort the nodes based on preference
            if (nodes.Count < 2) return nodes;

            if (level < sortingPreferences.Length)
            {
                switch (sortingPreferences[level])
                {
                    case "SPEED": return nodes.OrderBy(node => node.Speed).ToList();
                    case "WHEEL_NUMBER": return nodes.OrderBy(node => node.WheelNumber).ToList();
                    case "SEAT_NUMBER": return nodes.OrderBy(node => node.SeatNumber).ToList();
                    case "SPEED - WHEEL_NUMBER": return nodes.OrderBy(node => node.Speed).ThenBy(node => node.WheelNumber).ToList();
                    case "SPEED - WHEEL_NUMBER - SEAT_NUMBER": return nodes.OrderBy(node => node.Speed).ThenBy(node => node.WheelNumber).ThenByDescending(node => node.SeatNumber).ToList();
                    // And so on...
                    // And so on...
                }
            }
            // Unchanged (or nodes.OrderBy(some default order)
            return nodes;
    }

此代码示例仅在此处说明其中的白痴方法,即,您不会涵盖定义所有应用于对象列表的多重排序的所有条件组合,然后扩展此组合过程以涵盖每个对象的方向使用的标准。

问题是:如何定义一些排序设置/首选项,在其中将标准(速度,车轮编号,座位编号...)及其相关的排序方向应用于要对其进行多排序的对象列表?

例如,我的意思是可以通过以下方式给出首选项:

        new List<string>[]
                    {
                       new List<string>{ "SPEED", "ASCENDING" },           
                       new List<string>{ "WHEEL_NUMBER", "DESCENDING" },           
                       new List<string>{ "SEAT_NUMBER", "DESCENDING" },
                    },

这里有3个条件,但最终用户可以通过gui添加更多条件并为每个条件选择方向。

然后,如何考虑这些首选项以将其应用于对象列表?

2 个答案:

答案 0 :(得分:2)

创建一个代表字典,为您的查询提供OrderBy子句。每个委托都必须接受一个对象并返回要进行排序的值。

var map = new Dictionary<string,Func<Node,IComparable>>
{
    { "PropertyA", node => node.PropertyA },
    { "PropertyB", node => node.PropertyB }
};

然后将排序键也放入数组:

var sortBy = new string[] { "PropertyA", "PropertyB" };

按顺序排列后,可以通过查找每个键的委托来进行简单循环排序。您必须颠倒按键的顺序,因为处理的每个按键都将优先于先前的按键。

foreach (var sortKey in sortBy.Reverse())
{
    list = list.OrderBy( map[sortKey] );
}

这是在带有测试数据的上下文中的外观:

var map = new Dictionary<string,Func<Node,IComparable>>
{
    { "PropertyA", node => node.PropertyA },
    { "PropertyB", node => node.PropertyB }
};

IEnumerable<Node> list = new Node[]
{
    new Node { PropertyA = 1, PropertyB = "Z" },
    new Node { PropertyA = 2, PropertyB = "A" },
    new Node { PropertyA = 2, PropertyB = "B" }
};


var sortBy = new string[] { "PropertyA", "PropertyB" };

foreach (var sortKey in sortBy.Reverse())
{
    list = list.OrderBy( map[sortKey] );
}

foreach (var node in list)
{
    Console.WriteLine("{0} {1}", node.PropertyA, node.PropertyB);
}

输出:

1 Z
2 A
2 B

Working example on DotNetFiddle

答案 1 :(得分:0)

将您的OrderBy呼叫包装到ICommands(命令模式)中。首先,建立一个命令队列,其中每个命令都返回一个集合,然后在命令队列中循环。如果您的输入更改了commandslist,则必须重新构建。

您可以使用 fifo 队列来存储命令,当应删除命令时,您只需将其出队,直到找到要删除的命令,同时保留所有已出队的命令,然后使它们排队(减去已删除的命令)命令返回队列。

编辑:您可以将c#Func用于命令,并将键值对排序枚举和Func放入队列。

例如

Func<List<T>,List<T>>Queue<KeyValuePair<SortEnum,Func<List<T>,List<T>>>