简体   繁体   English

LINQ 递归查询以返回组的分层集

[英]LINQ recursive query to return hierarchical set of groups

Given a list of the following models给出以下模型的列表

public class Team
{
    public int TeamId { get; set; }
    public int ParentTeamId { get; set; }
}

I am trying to write a recursive linq query which will enable me to retrieve a heirarchy that looks like this我正在尝试编写一个递归 linq 查询,这将使我能够检索如下所示的层次结构

Team
    ChildTeams
Team
    Team
        ChildTeams

I've tried many approaches and seen many similar questions but none of them specifically helped me solve the problem.我尝试了很多方法,看到了很多类似的问题,但没有一个专门帮助我解决问题。 The latest attempt I tried went along these lines:我尝试的最新尝试是这样的:

private class TeamGrouping
{
    public int? ParentTeamId { get; set; }
    public IEnumerable<Team> ChildTeams { get; set; }
    public IEnumerable<TeamGrouping> Grouping { get; set; }
}

private IEnumerable<TeamGrouping> ToGrouping(IEnumerable<Team> teams)
{
    return teams.GroupBy(t => t.ParentTeamId, (parentTeam, childTeams) => new TeamGrouping {ParentTeamId = parentTeam, ChildTeams = childTeams});
}

private IEnumerable<TeamGrouping> ToGrouping(IEnumerable<TeamGrouping> teams)
{
    return teams.GroupBy(t => t.ParentTeamId, (parentTeam, childTeams) => new TeamGrouping{ParentTeamId = parentTeam, Grouping = childTeams});
}

I would pass the list of teams into the first ToGrouping(IEnumerable<Team>) and then subsequent returned groups into ToGrouping(IEnumerable<TeamGrouping>) but this is producing incorrect results.我会将团队列表传递到第一个ToGrouping(IEnumerable<Team>) ,然后将随后返回的组ToGrouping(IEnumerable<TeamGrouping>)ToGrouping(IEnumerable<TeamGrouping>)但这会产生不正确的结果。

Anyone have any advice or ideas?任何人有任何建议或想法?

So first, your TeamGrouping is actually a bit more complex than it needs to be.首先,您的TeamGrouping实际上比它需要的要复杂一些。 All it needs is the Team object and a sequence of itself for children:它所需要的只是Team对象和一个用于孩子的自身序列:

public class TeamNode
{
    public Team Value { get; set; }
    public IEnumerable<TeamNode> Children { get; set; }
}

Next we'll take our sequence of teams and create a node for each one.接下来,我们将采用我们的团队序列并为每个团队创建一个节点。 Then we'll use ToLookup to group them by their parent ID.然后我们将使用ToLookup按他们的父 ID 对它们进行分组。 (Your use of GroupBy is pretty darn close to this, but ToLookup will be easier.) Finally we can just set each node's children to be the lookup value for that node (note that ILookup will return an empty sequence if the key doesn't exist, so our leaves will be handled perfectly). (您对GroupBy使用与此非常接近,但ToLookup会更容易。)最后,我们可以将每个节点的子节点设置为该节点的查找值(请注意,如果键不存在, ILookup将返回一个空序列存在,所以我们的叶子将被完美处理)。 To finish it off we can return all of the top level nodes by just looking up all nodes with a parent ID of null .为了完成它,我们可以通过查找父 ID 为null所有节点来返回所有顶级节点。

public static IEnumerable<TeamNode> CreateTree(IEnumerable<Team> allTeams)
{
    var allNodes = allTeams.Select(team => new TeamNode() { Value = team })
        .ToList();
    var lookup = allNodes.ToLookup(team => team.Value.ParentTeamId);
    foreach (var node in allNodes)
        node.Children = lookup[node.Value.TeamId];
    return lookup[null];
}

Firstly you will need an object like this, so the Team object might be:首先你需要一个这样的对象,所以 Team 对象可能是:

public class Team
{
    public int? ParentId { get; set; }
    public IEnumerable<Team> ChildTeams { get; set; }
}

Then a recursive function:然后是递归函数:

private IEnumerable<Team> BuildTeams(IEnumerable<Team> allTeams, int? parentId)
{
    var teamTree = new List<Team>();
    var childTeams = allTeams.Where(o => o.ParentId == parentId).ToList();

    foreach (var team in childTeams)
    {
        var t = new Team();
        var children = BuildTeams(allTeams, team.TeamID);
        t.ChildTeams = children;
        teamTree.Add(t);
    }

    return teamTree ;
}

The first call passes a null for parent, and will pull all the teams that have a null parent :), though I notice your teams don't have a null for parent, so not sure how you identify the top level ones currently?第一次调用为父级传递一个null ,并将拉出所有父级为空的团队:),尽管我注意到您的团队没有父级的空值,所以不确定您目前如何识别顶级团队?

Another solution for when you are using Entity Framework objects you can handle root objects (Parent == null) by removing the Team objects that have a null parent on the first call.当您使用 Entity Framework 对象时,您可以通过删除在第一次调用时具有 null 父级的 Team 对象来处理根对象 (Parent == null) 的另一种解决方案。

public class Team
{
    int TeamID { get; set; }
    Team Parent; { get; set; }
}

public class TeamNode
{
    public Team Node { get; set; }
    public IEnumerable<TeamNode> Children { get; set; }
}

private List<Team> BuildTeams(List<Team> allTeams, int? parentId)
{
    List<TeamNodes> teamTree = new List<Team>();
    List<Team> childTeams;

    if (parentId == null)
    {
        childTeams = allTeams.Where(o => o.Parent == null).ToList();
        allTeams.RemoveAll(t => t.Parent == null);
    }
    else
    {
        childTeams = allTeams.Where(o => o.Parent.ID == parentId).ToList();
    }

    foreach (Team team in childTeams)
    {
        TeamNode teamNode = new Team();
        teamnode.Node = team;
        List<TeamNode> children = BuildTeams(allTeams, team.TeamID);
        teamNode.ChildTeams = children;
        teamTree.Add(t);
    }

    return teamTree ;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM