展平(带有级别编号)层次结构列表 [英] Flatten (with level numbers) a hierarchical list

查看:87
本文介绍了展平(带有级别编号)层次结构列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个指向自身的X类实例的集合(自我引用).
作为一个例子,我的课看起来像这样:

I have a collection of class X instances that point to themselves (self-referencing).
As an example, my class could look like this:

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

现在可以说我有一个X类实例的列表,这些实例可以将自己向下N级进行自我引用.

Now lets say I have a list of instances of class X, which can self-reference themselves N levels down.

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

基本上,我试图在C#中做一个递归自引用公共表表达式在SQL中的工作,这是为了弄平一个添加级别编号的层次结构列表.

Basically I am trying to do in C# what a recursive self-referencing common table expression would do in SQL, which is to flatten out a hierarchical list adding level numbers.

我找到了以下示例: https://stackoverflow.com/a/2118192/1171461 效果很好,但我仍然想不出如何仅从Nth级别获取元素.

I have found this example: https://stackoverflow.com/a/2118192/1171461 That works great but I still cant figure out how to get the elements ONLY from Nth level.

推荐答案

好吧,您可以修改链接示例中的Flatten方法,使其还包括一个级别编号,如下所示:

Well, you can modify the Flatten method in the linked example to include a level number as well, along these lines:

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

在此之后,您可以过滤出所需的级别:

after this, you can just filter out the required level:

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

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

Also, from @Servy's comment, since this is a breadth first approach (all of 1st level get done before any of the 2nd level is processed), we can use Skip/TakeWhile, like this:

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

这会延迟地进行枚举,因此根本不会处理层次结构后面的任何级别.

This will enumerate lazily, so any level further down the hierarchy will not be processed at all.

这篇关于展平(带有级别编号)层次结构列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆