6

我有一个包含 4 列的数据集。名称、密钥、父密钥、级别。我需要将此 DataTable 转换为树结构。我附上了一张图片,它会让你知道我想做什么。将 DataTable 转换为可用于生成树结构的对象的最有效方法是什么。请帮忙。

请注意:数据可以在 DataTable 中以任何顺序出现。是否可以在第一个 Level 列上对 dataTable 进行排序,然后在 parentKey 列上排序?我认为,如果我能做到这一点,将输出转换为树结构会很容易。

在此处输入图像描述

我添加了一个模拟数据集的类,并且我对数据表中的数据进行了排序。

namespace SortDataTable
{


    public class Program
    {
        private static void Main(string[] args)
        {
            DataTable table = new DataTable();
            table.Columns.Add("Name", typeof (string));
            table.Columns.Add("Key", typeof (string));
            table.Columns.Add("ParentKey", typeof (string));
            table.Columns.Add("Level", typeof (int));


            table.Rows.Add("A", "A1", null, 1);
            table.Rows.Add("B", "A2", "A1", 2);
            table.Rows.Add("C", "A3", "A1", 2);
            table.Rows.Add("D", "A4", "A1", 2);

            table.Rows.Add("E", "A5", "A2", 3);
            table.Rows.Add("F", "A6", "A5", 4);
            table.Rows.Add("G", "A7", "A3", 3);
            table.Rows.Add("H", "A8", "A4", 3);


            table.Rows.Add("I", "A9", "A4", 3);
            table.Rows.Add("J", "A10", "A4", 3);
            table.Rows.Add("K", "A11", "A10", 4);
            table.Rows.Add("L", "A12", "A10", 4);

            table.Rows.Add("M", "A13", "A12", 5);
            table.Rows.Add("N", "A14", "A12", 5);
            table.Rows.Add("O", "A15", "A10", 4);

            DataView view = table.DefaultView;

            // By default, the first column sorted ascending.
            view.Sort = "Level, ParentKey DESC";


            foreach (DataRowView row in view)
            {
                Console.WriteLine(" {0} \t {1} \t {2} \t {3}", row["Name"], row["Key"], row["ParentKey"], row["Level"]);
            }
            Console.ReadKey();

        }

    }


   public class Node<T>
    {
        internal Node() { }
        public T Item { get; internal set; }
        public int Level { get; internal set; }
        public Node<T> Parent { get; internal set; }
        public IList<Node<T>> Children { get; internal set; }



        public static IEnumerable<Node<T>> ToHierarchy<T>( IEnumerable<T> source, Func<T, bool> startWith, Func<T, T, bool> connectBy)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (startWith == null) throw new ArgumentNullException("startWith");
            if (connectBy == null) throw new ArgumentNullException("connectBy");
            return source.ToHierarchy(startWith, connectBy, null);
        }

        private static IEnumerable<Node<T>> ToHierarchy<T>(IEnumerable<T> source, Func<T, bool> startWith, Func<T, T, bool> connectBy, Node<T> parent)
        {
            int level = (parent == null ? 0 : parent.Level + 1);

            var roots = from item in source
                        where startWith(item)
                        select item;
            foreach (T value in roots)
            {
                var children = new List<Node<T>>();
                var newNode = new Node<T>
                {
                    Level = level,
                    Parent = parent,
                    Item = value,
                    Children = children.AsReadOnly()
                };

                T tmpValue = value;
                children.AddRange(source.ToHierarchy(possibleSub => connectBy(tmpValue, possibleSub), connectBy, newNode));

                yield return newNode;
            }
        }
    }





}
4

1 回答 1

5

我使用以下扩展方法来做这种事情:

    public class Node<T>
    {
        internal Node() { }
        public T Item { get; internal set; }
        public int Level { get; internal set; }
        public Node<T> Parent { get; internal set; }
        public IList<Node<T>> Children { get; internal set; }
    }

    public static IEnumerable<Node<T>> ToHierarchy<T>(
        this IEnumerable<T> source,
        Func<T, bool> startWith,
        Func<T, T, bool> connectBy)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (startWith == null) throw new ArgumentNullException("startWith");
        if (connectBy == null) throw new ArgumentNullException("connectBy");
        return source.ToHierarchy(startWith, connectBy, null);
    }

    private static IEnumerable<Node<T>> ToHierarchy<T>(
        this IEnumerable<T> source,
        Func<T, bool> startWith,
        Func<T, T, bool> connectBy,
        Node<T> parent)
    {
        int level = (parent == null ? 0 : parent.Level + 1);

        var roots = from item in source
                    where startWith(item)
                    select item;
        foreach (T value in roots)
        {
            var children = new List<Node<T>>();
            var newNode = new Node<T>
            {
                Level = level,
                Parent = parent,
                Item = value,
                Children = children.AsReadOnly()
            };

            T tmpValue = value;
            children.AddRange(source.ToHierarchy(possibleSub => connectBy(tmpValue, possibleSub), connectBy, newNode));

            yield return newNode;
        }
    }

在 aDataTable作为源的情况下,您可以像这样使用它:

var hierarchy =
    sourceTable.AsEnumerable()
               .ToHierarchy(row => row.IsNull("ParentKey"),
                            (parent, child) => parent.Field<int>("Key") ==
                                               child.Field<int>("ParentKey"))

hierarchy是一个IEnumerable<Node<DataRow>>

DataTable请注意,如果您在自身中定义父子关系,则您已经有了一个树结构......您只需要选择根(没有父项的项目)。

于 2013-08-13T08:23:18.733 回答