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
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.
Anyone have any advice or ideas?
So first, your TeamGrouping
is actually a bit more complex than it needs to be. All it needs is the Team
object and a sequence of itself for children:
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. (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). 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
.
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:
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With