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

时间:2013-11-15 15:30:42

标签: c# linq recursion

给出以下模型列表

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

我正在尝试编写一个递归的linq查询,这将使我能够检索看起来像这样的层次结构

Team
    ChildTeams
Team
    Team
        ChildTeams

我尝试了很多方法并看到了许多类似的问题,但没有一个能够帮助我解决问题。我试过的最新尝试遵循以下几点:

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});
}

我会将团队列表传递给第一个ToGrouping(IEnumerable<Team>),然后将后续返回的组传递到ToGrouping(IEnumerable<TeamGrouping>),但这会产生错误的结果。

任何人都有任何建议或想法吗?

3 个答案:

答案 0 :(得分:4)

首先,你的TeamGrouping实际上比它需要的要复杂一点。它需要的只是Team对象及其自身的序列:

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

接下来,我们将采用我们的团队序列并为每个团队创建一个节点。然后我们将使用ToLookup按父ID进行分组。 (您对GroupBy的使用非常接近,但ToLookup会更容易。)最后,我们可以将每个节点的子节点设置为该节点的查找值(请注意{{1}如果密钥不存在,将返回一个空序列,因此我们的叶子将被完美处理)。为了完成它,我们可以通过查找父ID为ILookup的所有节点来返回所有顶级节点。

null

答案 1 :(得分:0)

首先,您需要一个这样的对象,因此Team对象可能是:

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

然后是递归函数

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 ;
    }

第一个电话会传递null为父母,并且会拉出所有拥有空父母的球队:),虽然我注意到你的球队没有父母的空,所以不知道如何你现在确定了顶级的那些?

答案 2 :(得分:0)

使用Entity Framework对象时的另一种解决方案是,可以通过删除第一次调用时父级为空的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 ;
}