[英]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.