簡體   English   中英

獲取所有父母親子關系

[英]Get All Parents Child-Parent Relationship

我從外部來源收到以下列表(更像是一個連接表):

請注意,在某些情況下,一個人向多個人報告。 在這個樣本 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" },
    ...
};

為了讓 C001 的所有直接上級,我想出了以下內容:

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

這產生:

"B001"
"B003"

我如何包括所有上級,包括他的直接上級的上級等等?

C001 的預期結果:

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

接受的答案有效,但如果報告列表很大或針對它運行的查詢數量很大,則此解決方案效率低下。 它還在最壞的情況下分配了大量的子列表,從而產生了收集壓力。 你可以做得更好。

要做出有效的解決方案,首先要做的是做出更好的數據結構:

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

我們在這里想要的是一個多詞典 也就是說,給定報告的 id,字典返回一個直接經理的序列。 實現這個數據結構應該很簡單,或者,在各種包中都有可用的第三方實現。 請注意,如果 id 沒有管理器,我們假設多字典返回一個空序列,因此請確保保持不變。

一旦我們有了它,那么我們就可以制作一個遍歷算法

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

請注意,此解決方案不是遞歸的 這個答案的堆棧消耗是恆定的,不依賴於圖的拓撲結構。

現在我們有了這兩個工具,您的問題就可以直接解決了:

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

“跳過一個”從序列中刪除項目,因為您需要經理關系的傳遞閉包,而不是傳遞自反閉包。

練習:假設圖形可以包含一個循環。 您能否修改BreadthFirst以在第二次遇到循環時檢測並跳過枚舉? 你能用四行(或更少)的新代碼來完成嗎?

練習:類似​​地實現DepthFirst

DotNetFiddle 中測試,列表為靜態。
DotNetFiddle 中以列表為變量進行測試。

您可以使用遞歸函數查找經理,直到您再也找不到經理為止。 以下是查找整棵樹的一種方法。 如果您可以將直接下屬列表設為靜態,您就不必傳遞它。

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

    }

你會在 main 中使用上面的內容,

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

輸出

B001
A001
B003
A002

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM