首页 > 解决方案 > 按父母和孩子以及孩子的父母的顺序列表

问题描述

我正在尝试订购应该看起来像这样的列表

  1. 家长
    • Child1(同时是孩子和父母)
      • Child2(Child1 的孩子)
    • 孩子3

在使用包含有关 ID、ParentID 等信息的类时。

我正在尝试使用 LINQ 完成这项工作并尝试了不同的解决方案,但没有人完全工作,我知道递归函数会起作用(但真的不喜欢那样),有人可以帮助我使用 LINQ 吗?

我试过这段代码,但 Child2 没有出现。

List<Person> orderedList = new List<Person>();
            persons.ForEach(x => {
                if (x.ParentID == 0) {
                    orderedList.Add(x);
                    orderedList.AddRange(persons.Where(child => child.ParentID == x.Id));
                }
            });

对于那些投票“否定”的人来说,记住一开始没有人是编程的神,如果我来到这里,这意味着我在 x 小时内很难解决这个问题。而且,如果您认为我的英语不好,我已经知道了,我生来就不会说一口流利的英语,但愿意提供帮助的人会提供帮助。:)

完整代码

        public class Person{
            public int Id { get; set; }
            public string MenuName { get; set; }
            public int? ParentID { get; set; }
            public string isHidden { get; set; }
            public string LinkURL { get; set; }
        }
        public static List<Person> AddPersons(){
            var persons = new List<Person>();
            using (var reader = new StreamReader(@"C:\Users\AceDuk\Desktop\Navigation.csv")){
                var line = reader.ReadLine(); //Da se izbegne headerot
                while ((line = reader.ReadLine()) != null){
                    var values = line.Split(';');

                    if (values[2] == "NULL") {
                        values[2] = "0";
                    }

                    persons.Add(new Person(){
                        Id = Int32.Parse(values[0]),
                        MenuName = values[1],
                        ParentID = Int32.Parse(values[2]),
                        isHidden = values[3],
                        LinkURL = values[4]
                    });
                }
            }
            persons.RemoveAll(x => x.isHidden == "True"); //Izbrisi gi site sto se hidden ne gi pokazuvaj..
            //persons = persons.OrderBy(x => x.MenuName).ToList(); //Ordered
            persons = persons.OrderBy(x => x.LinkURL).ToList(); //Ordered
            return persons;
        }
        static void Main(string[] args){
            List<Person> persons = AddPersons();

            List<Person> orderedList = new List<Person>();
            persons.ForEach(x => {
                if (x.ParentID == 0) {
                    orderedList.Add(x);
                    orderedList.AddRange(persons.Where(child => child.ParentID == x.Id));
                }
            });

            foreach (var item in orderedList) {
                Console.WriteLine(item.MenuName);
            }
        }

标签: c#linq

解决方案


通过扩展链表创建双端队列(deque)数据结构:

public class Deque<T> : LinkedList<T> {
    public void Enqueue(T item) => AddLast(item);
    public T Dequeue() {
        var item = First.Value;
        RemoveFirst();
        return item;
    }
    public void EnqueueRange(IEnumerable<T> items) {
        foreach (var item in items)
            Enqueue(item);
    }

    public void Push(T item) => AddFirst(item);
    public T Pop() => Dequeue();
    public void PushRange(IEnumerable<T> items) {
        foreach (var item in items)
            Push(item);
    }

    public T Peek() => Last.Value;
}

现在,使用以下方法创建从Id到子级的映射ToLookup

var childrenDictionary = persons.Where(p => p.ParentID != 0).ToLookup(p => p.ParentID);

最后,使用双端队列创建工作列表并添加所有根节点:

var workDeque = new Deque<Person>();
workDeque.EnqueueRange(persons.Where(p => p.ParentID == 0));

现在您可以通过workDeque,将每个根节点添加到orderedPersons,然后将节点的子节点推workDeque送到下一步工作:

var orderedPersons = new List<Person>();

while (workDeque.Count > 0) {
    var nextPerson = workDeque.Dequeue();
    orderedPersons.Add(nextPerson);
    workDeque.PushRange(childrenDictionary[nextPerson.Id]);
}

推荐阅读