从人员树生成团队树的正确递归方法是什么?

本文关键字:递归方法 是什么 团队 | 更新日期: 2023-09-27 18:33:07

我有一个具有以下属性的 Person 对象

public class Person
{
   public string FirstName;
   public string LastName;
   public string TeamName;
   public Person Manager;
   public IEnumerable<Person> DirectReports;
}

因此,鉴于我可以通过递归循环遍历每个人的直接报告及其直接报告等来创建人员层次结构,如下所示:

foreach (Person direct in person.DirectReports)
{
       foreach (Person subDirect in direct.DirectReports)
       {
            etc . . .
       }
}

根据这些数据,我现在正在尝试转换以生成 Teams 层次结构,所以我有这样的东西:

 public class Team
 {
    public string TeamName;
    public IEnumerable<Team> SubTeams;
    public IEnumerable<Person> PeopleInTeam;
}

请注意,如果人员的直接下属可能具有也可能不具有相同的团队名称,则人员层次结构中的级别数不一定与团队层次结构中的级别数相同

例如:

|人|家长 |团队名称 | |比尔 | '"'"          |管理 | |乔 | 比尔 |管理 | |斯科特 | 比尔 |市场营销 | |吉姆 | 比尔 |技术 |  |马克 | 斯科特 |市场营销 ||鲍勃 | 乔 |市场营销 |

所以转换后,我会有一个叫做"管理"的团队,里面有 2 个人。 它将在子团队数组中有 2 个项目(一个用于营销,一个用于技术(。 这些团队中的每一个在子团队数组中都没有条目

有效地采用此人员层次结构并将其转换为团队层次结构的最佳方法是什么? 我在下面列出了代码,但它似乎失败了。现在,我正在循环浏览每个人和他们的直接下属,并创建一个团队字典,并一次将每个人添加到一个团队中,但这似乎很慢。 这是一个例子..

 Dictionary<string, Team> teams = new Dictionary<string, Team>();
 foreach (Person direct in person.DirectReports)
 {
       if (teams.ContainsKey(direct.TeamName)
       {
              var team = teams[direct.TeamName];
              team.People.Add(direct);
       }
       else
       {
            var team = new Team(){TeamName = direct.TeamName};
            team.People.Add(direct);
            teams[direct.TeamName] = team;
       }
       foreach (Person subDirect in direct.DirectReports)
       {
            etc . . .
       }
}

从人员树生成团队树的正确递归方法是什么?

下面是一个可以用作框架的基本解决方案。

这并不理想:特别是,Dictionary<string, string>不是一个好的数据结构,因为我们最终只是遍历所有值。构建类似从部门映射到其子部门名称的Dictionary<string, List<string>>之类的东西会更有用。

但它应该给你一个工作的基础。

void Main()
{
    var bill = new Person { FirstName = "Bill", TeamName = "Management" };
    var joe = new Person { FirstName = "Joe", Manager = bill, TeamName = "Management" };
    var scott = new Person { FirstName = "Scott", Manager = bill, TeamName = "Marketing" };
    var jim = new Person { FirstName = "Jim", Manager = bill, TeamName = "Technology" };
    var mark = new Person { FirstName = "Mark", Manager = scott, TeamName = "Marketing" };
    var bob = new Person { FirstName = "Bob", Manager = joe, TeamName = "Marketing" };
    var ted = new Person { FirstName = "Ted", Manager = jim, TeamName = "IT Support" };
    var people = new[] { bill, joe, scott, jim, mark, bob, ted };
    var teamParents = people.Select (p => new { Team = p.TeamName, ParentTeam = p.Manager == null ? null : p.Manager.TeamName });
    // don't let a team be its own parent
    teamParents = teamParents.Where (p => !p.Team.Equals(p.ParentTeam));
    // make sure they're all unique
    teamParents = teamParents.Distinct();
    // put it in a dictionary
    var teamHierarchy = teamParents.ToDictionary (p => p.Team, q => q.ParentTeam);
    foreach (string root in teamHierarchy.Where (h => h.Value == null).Select (h => h.Key))
    {    
        PrintSubteams(teamHierarchy, 0, root);
    }    
}
private void PrintSubteams(Dictionary<string, string> hierarchy, int level, string root)
{
    for (int i = 0; i < level; i++)
    {
        Console.Write("    ");
    }
    Console.WriteLine(root);
    foreach (string child in hierarchy.Where (h => h.Value == root).Select(h => h.Key))
    {
        PrintSubteams(hierarchy, level + 1, child);
    }
}
public class Person
{
    public string FirstName;
    public string LastName;
    public string TeamName;
    public Person Manager;
    public IEnumerable<Person> DirectReports;
}

输出如下:

管理    营销    科技        信息技术支持

(我添加了 IT 支持团队,使其更有趣。

如果你在树的顶部只有一个人员列表,那么我们可以试试这个:

public void ConstructTeams(IEnumerable<Person> topPeople,
    Dictionary<string, Team> teamsByName)
{
    foreach (Person person in topPeople)
    {
        // Add person to their team. Create one if it doesn't exist
        Team matchingTeam;
        if (teamsByName.TryGetValue(person.TeamName, out matchingTeam))
        {
            matchingTeam.PeopleInTeam.Add(person);
        }
        else
        {
            // Create a new team and update its parent team
            matchingTeam = new Team
            {
                TeamName = direct.TeamName
                PeopleInTeam = new List<Person>()
            };
            matchingTeam.PeopleInTeam.Add(person);
            teamsByName.Add(matchingTeam.TeamName, matchingTeam);
            // The manager's team should already exist because we traversing the
            // Person tree/forest from the roots to the leaves
            if (person.Manager != null)
            {
                teamsByName[person.Manager.TeamName].SubTeams.Add(matchingTeam);
            }
        }
        // Recursively fill in the direct reports
        ConstructTeams(person.DirectReports, teamsByName);
    }
}

如果您有所有人的平面列表,这是一个解决方案。

public IEnumerable<Team> ConstructTeams(IEnumerable<Person> allPeople)
{
    var teams = allPeople.GroupBy(person => person.TeamName)
        .Select(grouping => new Team
        {
            TeamName = grouping.Key,
            // I think this works, but IGroupings are weird and I don't
            // have a compiler with me
            PeopleInTeam = grouping.ToList()
        });
    // Set SubTeams
    // Group the teams by parent team. We assumed that
    // no team has two parent teams
    var teamsGroupedByParentTeam = teams
        .GroupBy(team => team.PeopleInTeam.First().TeamName);
    foreach (var groupOfTeams in teamsGroupedByParentTeam)
    {
        var parentTeam = teams.Single(team => team.TeamName == teamsGroupedByParentTeam.TeamName);
        parentTeam.SubTeams = groupOfTeams.ToList();
    }
    return teams;
}