从树结构的每个级别获取属性

时间:2014-12-30 16:37:12

标签: c# .net generics

我有一个对象列表,其中列表中的每个对象可能在其下面有另一个相同类型的列表(一个非常标准的树结构)。我正在寻找一种方法,通常从这个树的每个级别(因为我想将其中的几个)拉到一个扁平列表中的属性。

对象示例:

public class Group {

    public List<Group> SubGroups { get; set; }
    public List<OtherStuff> OtherStuffs { get; set; }
    public OtherThing Thing { get; set; }

}

像这样:

groups.FlattenProperty(g => g.Thing)

但仍然可以

groups.FlattenProperty(g => g.OtherStuffs).SelectMany(s => s);

我对数据结构没有太多控制权,因为它是遗留系统的一部分,因此修改结构实际上不是一种选择。

是否存在可以合理重用的现有解决方案?

2 个答案:

答案 0 :(得分:3)

我相信这就是你要找的东西。

public static IEnumerable<T> FlattenProperty<T>(this Group g, Func<Group,T> transform)
{
    yield return transform(g);
    foreach(var item in g.SubGroups.SelectMany(sub => sub.FlattenProperty(transform)))
       yield return item;
}

这将获取您的lambda并将其应用于初始Group对象并生成结果。然后它将以递归方式为SubGroups调用自己。 SelectMany将从递归调用返回的IEnumerable中提取单个项目,每个项目都将被提升。

或者,如果您想传入IEnumerable<Group>,则可以将其更改为

public static IEnumerable<T> FlattenProperty<T>(
    this IEnumerable<Group> groups, 
    Func<Group,T> transform)
{
    foreach(Group g in groups)
    {
        yield return transform(g);
        foreach(var item in g.SubGroups.FlattenProperty(transform))
            yield return item;
    }
}

答案 1 :(得分:1)

轻微改编juharr的答案,允许我最终使用的任意对象。

public static IEnumerable<TV> FlattenTreeProperty<T, TV>(this IEnumerable<T> collectionToRecurse, Func<T, IEnumerable<T>> childrenSelector, Func<T, TV> propertySelector)
{
    var itemsToRecurse = (collectionToRecurse != null ? collectionToRecurse as IList<T> ?? collectionToRecurse.ToList() : new List<T>());

    if (!itemsToRecurse.Any())
    {
        yield break;
    }

    foreach (var item in itemsToRecurse.Select(propertySelector).Where(i => i != null))
    {
        yield return item;
    }

    foreach (var itemList in itemsToRecurse.Select(childrenSelector).Where(i => i != null))
    {
        foreach (var item in itemList.FlattenTreeProperty(childrenSelector, propertySelector))
        {
            yield return item;
        }
    }
}

像那样使用:

groups.FlattenTreeProperty(g => g.SubGroups, g => g.OtherStuffs)

它上面的一个重载清理它给了我正是我想要的东西。

编辑:修复了稀疏树,如果任何子项为空,SelectMany会抛出异常