【问题标题】:LINQ query through several object "layers" in an IList?通过 IList 中的多个对象“层”进行 LINQ 查询?
【发布时间】:2012-12-08 16:54:58
【问题描述】:

我有一个List<Category>,其中Category 是:

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&lt;Category&gt;,它可以包含子类别,而这些子类别可以包含子类别,等等...

我知道我可以像这样查询第一个“层”:

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

如何通过Id 有效地查询特定的Category,对象树中可能有 3、4 或 5 层(最多有 3 层,但我想知道以供将来参考) ?

编辑

此外,如果我只有 3 层深的子类别的 Id,我怎么能得到整个对象树,一直到顶层 Category

【问题讨论】:

    标签: c# linq


    【解决方案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());
    

    如果您愿意,可以将此方法转换为扩展。

    【讨论】:

    • 完美!非常感谢!
    • 如果你真的只期待一个(或零)结果,你应该使用 SingleOrDefault() 你忽略重复,看起来它们要么是错误,要么应该是结果的一部分
    【解决方案2】:

    您可以编写如下扩展方法将Category 展平为IEnumerable&lt;Category&gt;

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

    然后你在IEnumerable&lt;Category&gt; 上随意使用Linq:

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

    【讨论】:

      【解决方案3】:

      如果您有不确定的嵌套级别,则需要通过Categories 递归,即使您有固定的嵌套级别,对于任何超过 2-3 层的嵌套级别,它都是值得的递归。

      Linq 并没有真正表达递归的方式,尽管这篇文章谈到了使用 Linq2Xml 特性来实现它:Expressing recursion in LINQ

      如果你能够修改类本身,你可以实现一个GetChildById风格的方法来递归地扫描子Categories

      【讨论】:

        猜你喜欢
        • 2019-06-14
        • 2016-10-16
        • 1970-01-01
        • 1970-01-01
        • 2021-09-08
        • 1970-01-01
        • 1970-01-01
        • 2021-09-25
        • 1970-01-01
        相关资源
        最近更新 更多