2012-12-08 44 views
1

我有一個List<Category>其中Category是:通過IList中的幾個對象「圖層」進行LINQ查詢?

public class Category { 
    public List<Category> Categories { get; set; } // this holds sub-categories 
    public string Name { get; set; } 
    public string Icon { get; set; } 
    public string Id { get; set; } 
} 

由於Categories本身是另一個List<Category>它可能包含的子類別,而這些子類可以包含子類,等等...

我知道我可以查詢過去的第一個「層」,例如:

Categories.Where(x => x.Categories.Any(c => c.Id == id)).FirstOrDefault(); 

我怎樣纔能有效地查詢特定Category通過Id,或許是對象樹中的3,4或5層深度(最多隻有3層,但爲了將來的參考我想知道)?

編輯

此外,我怎麼能得到整個對象樹,一路攀升到頂級Category,如果我只有一個子類3層深的Id

回答

1

這將遞歸遍歷類別直到找到類別匹配通過的ID(如果有的話)。對發現類別完整路徑將被退回(即像麪包屑菜單一樣):

static IEnumerable<Category> GetById(IEnumerable<Category> categories, string id) 
{ 
    if (categories == null || !categories.Any()) 
     yield break; 

    Category result = categories.FirstOrDefault(c => c.Id == id); 
    if (result != null) 
    { 
     yield return result; 
     yield break; 
    } 

    foreach (var category in categories) 
    { 
     var subCategories = GetById(category.Categories, id); 
     if (subCategories.Any()) // we have found the category 
     { 
      yield return category; // return current category first 

      foreach (var subCategory in subCategories)      
       yield return subCategory;     

      yield break; // don't search in other categories 
     } 
    } 
} 

用法:

IEnumerable<Category> result = GetById(categories, id); 
// Food > Beer > Kilkenny 
string breadcrumbs = String.Join(" > ", result.Select(c => c.Name).ToArray()); 

你可以,如果你想這種方法轉換爲擴展名。

+1

完美!非常感謝! – Chaddeus

+0

如果你真的只希望得到一個(或零)結果,你應該使用SingleOrDefault(),你忽略了重複,看起來它們要麼是錯誤,要麼應該是結果的一部分 –

0

如果您的嵌套級別不確定,並且即使嵌套級別固定,對於嵌套級別超過2-3級的任何級別,您都需要遞歸通過Categories,這將值得遞歸。

的LINQ實際上並沒有表達遞歸的一種方式,但關於使用Linq2Xml功能來實現它這個帖子會談:Expressing recursion in LINQ

如果你能夠修改類本身,你可以實現一個GetChildById風格方法遞歸掃描子節點Categories

1

你可以寫一個擴展方法像下面扁平化CategoryIEnumerable<Category>

public static IEnumerable<Category> Flatten(this Category category) 
{ 
    if (category.Categories != null) 
    { 
     foreach (var sub in category.Categories) 
     { 
      foreach (var subSub in sub.Flatten()) 
       yield return subSub; 
     } 
    } 
    yield return category; 
} 

然後你使用Linq的IEnumerable<Category>只要你喜歡:

var filtered = categoryList.SelectMany(x => x.Flatten()) 
          .Where(x => x.Id == id);