简体   繁体   English

获取所有父母亲子关系

[英]Get All Parents Child-Parent Relationship

I am receiving the following list (which is more like a join table) from external source:我从外部来源收到以下列表(更像是一个连接表):

Note that there are cases where a person reports to more than one.请注意,在某些情况下,一个人向多个人报告。 In this sample C001.在这个样本 C001 中。

List<DirectReport> list = new List<DirectReport>()
{
    new DirectReport(){ EmployeeId = "B001", ReportsTo = "A001" },
    new DirectReport(){ EmployeeId = "B002", ReportsTo = "A001" },
    new DirectReport(){ EmployeeId = "B003", ReportsTo = "A002" },
    new DirectReport(){ EmployeeId = "B004", ReportsTo = "A003" },
    new DirectReport(){ EmployeeId = "C001", ReportsTo = "B001" },
    new DirectReport(){ EmployeeId = "C001", ReportsTo = "B003" },
    new DirectReport(){ EmployeeId = "C002", ReportsTo = "B002" },
    ...
};

To get all immediate superiors for C001, I came up with the following:为了让 C001 的所有直接上级,我想出了以下内容:

IEnumerable<string> listC001sSuperiors = list.Where(x => x.EmployeeId == "C001").Select(y => y.ReportsTo);

This yields:这产生:

"B001"
"B003"

How do I include all superiors including his immediate superior's superiors and so on?我如何包括所有上级,包括他的直接上级的上级等等?

Desired result for C001: C001 的预期结果:

"B001"
"B003"
"A001"
"A002"

The accepted answer works, but this solution is inefficient if the list of reports is large or if the number of queries run against it is large.接受的答案有效,但如果报告列表很大或针对它运行的查询数量很大,则此解决方案效率低下。 It also allocates a large number of sub-lists in the worst cases, which produces collection pressure.它还在最坏的情况下分配了大量的子列表,从而产生了收集压力。 You can do much better.你可以做得更好。

To make an efficient solution, the first thing to do is to make a better data structure:要做出有效的解决方案,首先要做的是做出更好的数据结构:

static IDictionary<string, IEnumerable<string>> ToDictionary(
  this List<DirectReport> reports)
{
  // Fill this in
}

What we want here is a multidictionary .我们在这里想要的是一个多词典 That is, given the id of a report, the dictionary returns a sequence of direct managers.也就是说,给定报告的 id,字典返回一个直接经理的序列。 It should be straightforward to implement this data structure, or, there are third party implementations available in various packages.实现这个数据结构应该很简单,或者,在各种包中都有可用的第三方实现。 Note that we're assuming that the multidictionary returns an empty sequence if an id has no managers, so make sure that invariant is maintained.请注意,如果 id 没有管理器,我们假设多字典返回一个空序列,因此请确保保持不变。

Once we have that, then we can make a traversal algorithm :一旦我们有了它,那么我们就可以制作一个遍历算法

static IEnumerable<T> BreadthFirst(
  T item,
  Func<T, IEnumerable<T>> children
)
{
  var q = new Queue<T>();
  q.Enqueue(item);
  while(q.Count != 0)
  {
    T t = q.Dequeue();
    yield return t;
    foreach(T child in children(t))
      q.Enqueue(child);
  }
}

Note that this solution is not recursive .请注意,此解决方案不是递归的 The stack consumption of this answer is constant, not dependent on the topology of the graph.这个答案的堆栈消耗是恒定的,不依赖于图的拓扑结构。

Now that we have these two tools, your problem can be solved in a straightforward manner:现在我们有了这两个工具,您的问题就可以直接解决了:

var d = reports.ToDictionary();
var r = BreadthFirst("C001", x => d[x]).Skip(1);

The "skip one" removes the item from the sequence, since you want the transitive closure of the manager relation, not the transitive reflexive closure. “跳过一个”从序列中删除项目,因为您需要经理关系的传递闭包,而不是传递自反闭包。

Exercise: Suppose the graph can contain a cycle.练习:假设图形可以包含一个循环。 Can you modify BreadthFirst to detect and skip enumerating a cycle the second time it is encountered?您能否修改BreadthFirst以在第二次遇到循环时检测并跳过枚举? Can you do it in four (or fewer) lines of new code?你能用四行(或更少)的新代码来完成吗?

Exercise: Implement DepthFirst similarly.练习:类似​​地实现DepthFirst

Tested in DotNetFiddle with list as static.DotNetFiddle 中测试,列表为静态。
Tested in DotNetFiddle with list as variable.DotNetFiddle 中以列表为变量进行测试。

You can use a recursive function to look up managers until you can no longer get one.您可以使用递归函数查找经理,直到您再也找不到经理为止。 Following is one way of looking up the entire tree.以下是查找整棵树的一种方法。 If you can make list of direct reports static, you wont have to pass it around.如果您可以将直接下属列表设为静态,您就不必传递它。

    public static List<DirectReport> list = new List<DirectReport>()
     {
      new DirectReport() { EmployeeId = "B001", ReportsTo = "A001"},
      new DirectReport(){EmployeeId = "B002", ReportsTo = "A001"},
      new DirectReport() {EmployeeId = "B003", ReportsTo = "A002"},
      new DirectReport() {EmployeeId = "B004", ReportsTo = "A003"},
      new DirectReport() {EmployeeId = "C001", ReportsTo = "B001"},
      new DirectReport() {EmployeeId = "C001", ReportsTo = "B003"},
      new DirectReport() {EmployeeId = "C002", ReportsTo = "B002"},
      new DirectReport() {EmployeeId = "A002", ReportsTo = "C001"},
     };

    public class DirectReport
    {
        public string EmployeeId { get; set; }
        public string ReportsTo { get; set; }
    }

    public static void ReportsTo(string employeeId, List<string> results)
    {
        var managers = list.Where(x => x.EmployeeId.Equals(employeeId)).Select(x => x.ReportsTo).ToList();
        if (managers != null && managers.Count > 0)
            foreach (string manager in managers)
            {
                if (results.Contains(manager))
                    continue;

                results.Add(manager);
                ReportsTo(manager, results);
            }

    }

and you would use the above in main like,你会在 main 中使用上面的内容,

    List<string> results = new List<string>();
    ReportsTo("C001", results);
    Console.WriteLine(string.Join(Environment.NewLine, results));

Output输出

B001
A001
B003
A002

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

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