C#对父/子列表进行排序,以生成平面输出

C#对父/子列表进行排序,以生成平面输出,c#,algorithm,linq,parent-child,lambda,C#,Algorithm,Linq,Parent Child,Lambda,我有这个模型: public class Node { public int Id { get; set; } public string Name { get; set; } public int? ParentId { get; set; } } 我有来自数据库查询的以下数据: nodes.Add(new Node { Id = 1, Name = "Node #1", ParentId = null }); nodes.Add(n

我有这个模型:

public class Node 
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? ParentId { get; set; }
}
我有来自数据库查询的以下数据:

        nodes.Add(new Node { Id = 1, Name = "Node #1", ParentId = null });
        nodes.Add(new Node { Id = 2, Name = "Node #2", ParentId = 1 });
        nodes.Add(new Node { Id = 3, Name = "Node #3", ParentId = 2 });
        nodes.Add(new Node { Id = 4, Name = "Node #4", ParentId = null });
        nodes.Add(new Node { Id = 5, Name = "Node #5", ParentId = 2 });
        nodes.Add(new Node { Id = 6, Name = "Node #6", ParentId = 2 });
        nodes.Add(new Node { Id = 7, Name = "Node #7", ParentId = 1 });
        nodes.Add(new Node { Id = 8, Name = "Node #8", ParentId = 5 });
        nodes.Add(new Node { Id = 9, Name = "Node #9", ParentId = 4 });
        nodes.Add(new Node { Id = 10, Name = "Node #10", ParentId = 4 });
我想对列表进行排序并保持平面结构。我期望的结果是:

        // 1  - Node #1  => NULL
        // 2  - Node #2  => 1
        // 3  - Node #3  => 2
        // 5  - Node #5  => 2
        // 8  - Node #8  => 5
        // 6  - Node #6  => 2
        // 7  - Node #7  => 1
        // 4  - Node #4  => NULL
        // 9  - Node #9  => 4
        // 10 - Node #10 => 4
我指的是这个,但我没有得到我想要的结果


有什么帮助吗?

您可以尝试使用
SelectMany
在LINQ中创建递归查询,如下所示:

IEnumerable<Node> Recurcive(List<Node> nodeList, int? parentId)
{
    return nodeList
        .Where(x => x.ParentId == parentId)
        .SelectMany(x =>
                  new[] { new Node
                    { Id = x.Id, Name = x.Name, ParentId = x.ParentId } }
                        .Concat(Recurcive(nodeList, x.Id)));
}

foreach (var node in Recurcive(nodes, null))
    Console
     .WriteLine($"Id : {node.Id}\t, Name = {node.Name}\t, Parent = {node.ParentId}");
我想你想要这个

 nodes.OrderBy(n => n.ParentID ?? n.Id)
      .ThenBy(n => n.Id);

我会这样做:

var nodes = new List<Node>()
{
    new Node { Id = 1, Name = "Node #1", ParentId = null },
    new Node { Id = 2, Name = "Node #2", ParentId = 1 },
    new Node { Id = 3, Name = "Node #3", ParentId = 2 },
    new Node { Id = 4, Name = "Node #4", ParentId = null },
    new Node { Id = 5, Name = "Node #5", ParentId = 2 },
    new Node { Id = 6, Name = "Node #6", ParentId = 2 },
    new Node { Id = 7, Name = "Node #7", ParentId = 1 },
    new Node { Id = 8, Name = "Node #8", ParentId = 5 },
    new Node { Id = 9, Name = "Node #9", ParentId = 4 },
    new Node { Id = 10, Name = "Node #10", ParentId = 4 },
};

var lookup = nodes.ToLookup(x => x.ParentId);

IEnumerable<Node> Flatten(int? parentId)
{
    foreach (var node in lookup[parentId])
    {
        yield return node;
        foreach (var child in Flatten(node.Id))
        {
            yield return child;
        }
    }   
}

var output = Flatten(null).ToArray();
var节点=新列表()
{
新节点{Id=1,Name=“Node#1”,ParentId=null},
新节点{Id=2,Name=“Node#2”,ParentId=1},
新节点{Id=3,Name=“Node#3”,ParentId=2},
新节点{Id=4,Name=“Node#4”,ParentId=null},
新节点{Id=5,Name=“Node#5”,ParentId=2},
新节点{Id=6,Name=“Node#6”,ParentId=2},
新节点{Id=7,Name=“Node#7”,ParentId=1},
新节点{Id=8,Name=“Node#8”,ParentId=5},
新节点{Id=9,Name=“Node#9”,ParentId=4},
新节点{Id=10,Name=“Node#10”,ParentId=4},
};
var lookup=nodes.ToLookup(x=>x.ParentId);
IEnumerable展平(int?parentId)
{
foreach(查找[parentId]中的var节点)
{
收益回报节点;
foreach(展平中的var子节点(node.Id))
{
退换子女;
}
}   
}
var输出=展平(null).ToArray();
这一点递归给了我:


为什么节点8不在末尾?@Hogan-这里真正的问题是节点7不应该出现在节点2之后。列表中的其他所有内容看起来都是深度优先搜索,除了那个节点。我想如果OP告诉我们他们到底在做什么就好了。你是对的@Hogan,我在填充output@MKR-OP的输出是深度优先和广度优先搜索的不匹配。我认为OP的输出是错误的。如果他试图进行深度优先搜索,他的节点#7应该出现在节点#6之后。@MKR-请不要这样删除您的评论。这会让你很难理解谈话内容。@MKR-我也喜欢这样。但删除评论往往会降低答案的价值。这应该避免。这就是我认为发生在这里的事情。是的,你是对的,节点#7应该出现在节点#6之后,谢谢你。我不认为
ThenBy
对OP提供的数据有任何影响。@Issam我修改过的答案应该符合你修改过的逻辑。
var nodes = new List<Node>()
{
    new Node { Id = 1, Name = "Node #1", ParentId = null },
    new Node { Id = 2, Name = "Node #2", ParentId = 1 },
    new Node { Id = 3, Name = "Node #3", ParentId = 2 },
    new Node { Id = 4, Name = "Node #4", ParentId = null },
    new Node { Id = 5, Name = "Node #5", ParentId = 2 },
    new Node { Id = 6, Name = "Node #6", ParentId = 2 },
    new Node { Id = 7, Name = "Node #7", ParentId = 1 },
    new Node { Id = 8, Name = "Node #8", ParentId = 5 },
    new Node { Id = 9, Name = "Node #9", ParentId = 4 },
    new Node { Id = 10, Name = "Node #10", ParentId = 4 },
};

var lookup = nodes.ToLookup(x => x.ParentId);

IEnumerable<Node> Flatten(int? parentId)
{
    foreach (var node in lookup[parentId])
    {
        yield return node;
        foreach (var child in Flatten(node.Id))
        {
            yield return child;
        }
    }   
}

var output = Flatten(null).ToArray();