展平(带有级别编号)分层列表

时间:2013-12-17 18:15:40

标签: c# hierarchical-data

我有一组指向自己的类X实例(自引用)。
作为一个例子,我的班级看起来像这样:

public class X {
   string Name {get;set;}
   List<X> Children {get;}
}

现在假设我有一个X类实例列表,它可以自我引用N级别。

我的问题是:如何在我的列表中从第N级获取X的实例?

基本上我在C#中尝试用递归自引用公用表表达式在SQL中做什么,这是为了压缩添加级别号的分层列表。

我找到了这个例子:https://stackoverflow.com/a/2118192/1171461 这很好但我仍然无法弄清楚如何从第N级获得元素。

1 个答案:

答案 0 :(得分:2)

嗯,您可以修改链接示例中的Flatten方法,也可以包含一个级别编号,如下所示:

public class Leveled<T> 
{
    public T Item {get; set;}
    public int Level {get; set;}
}

public static IEnumerable<Leveled<T>> ToLeveled<T>(this IEnumerable<T> sequence,
                                            int level)
{
   return sequence.Select(item => new Leveled<T>{ Item = item, Level = level});
}

public static IEnumerable<Leveled<T>> FlattenLeveled<T>(this IEnumerable<T> sequence, 
                                            Func<T, IEnumerable<T>> childFetcher)
{
    var itemsToYield = new Queue<Leveled<T>>(sequence.ToLeveled(0));
    while (itemsToYield.Count > 0)
    {
        var leveledItem = itemsToYield.Dequeue();
        yield return leveledItem;

        var children = childFetcher(leveledItem.Item).ToLeveled(leveledItem.Level + 1);
        if (children != null)
        { 
            foreach (var child in children) 
               itemsToYield.Enqueue(child);
        }
    }
}

之后,您可以过滤掉所需的级别:

var thirdLevel = myCollection
           .FlattenLeveled(item => item.Children)
           .Where(leveledItem => leveledItem.Level == 2)
           .Select(leveledItem => leveledItem.Item)

此外,来自@ Servy的评论,因为这是一个广度优先的方法(所有第一级都在第二级处理之前完成),我们可以使用Skip / TakeWhile,如下所示:

public static IEnumerable<T> GetHierarchyLevel<T>(this IEnumerable<T> sequence, Func<T, IEnumerable<T>> childFetcher, int level)
{
  return sequence.FlattenLeveled(childFetcher)
                 .SkipWhile(li => li.Level < level)
                 .TakeWhile(li => li.Level == level)
                 .Select(li => li.Item);
}

这会懒惰地列举,因此层次结构中的任何级别都不会被处理。