深度优先使用LINQ展平对象层次结构的集合

我有一个对象层次结构(MasterNode – > ChildNodes),其中主节点和子节点属于同一类型,并且只有两个级别(顶级和子级)像这样(’A’是D,E和F的父级,’ B’是G的父母等)

A--+ | D | E | F | B--+ | G | C--+ H I 

假设我有一个MasterNodes作为父对象(A,B,C)的IEnumerable,给定一个父对象X我可以通过X.children得到它的子节点的IEnumerable

我知道我可以使用SelectMany方法或使用枚举所有叶子(子节点)

 from parent in Masternodes from child in parent.children select child 

这将给我这个序列:

 [D,E,F,G,H,I] 

,但这不是我要求的。

什么是LINQ查询以获取MasterNodes集合中对象的深度优先序列? (返回第一个父母,然后是所有孩子,然后是下一个父母,然后是所有孩子等等)

预期的结果应该是这样的序列:

 [A,D,E,F,B,G,C,H,I] 

更新

我要求纯粹的.NET就绪LINQ。 我知道我可以定义自己的方法来做事,但我想要的东西只基于框架提供的方法。

如果您需要两个以上的层次结构级别,则可以使用以下扩展方法,该方法以递归方式通过对象图形:

 public static IEnumerable Flat(this IEnumerable l, Func> f) => l.SelectMany(i => new T[] { i }.Concat(f(i).Flat(f))); 

它使用一个函数将给定的IEnumerable变平,该函数将T映射到描述父数据的IEnumerable – >数据的子关系。

深度优先展平是通过将每个元素与其子树结合,然后将它们与SelectMany连接来完成的。

你可以像这样使用它:

 var flattened = Masternodes.Flat(c => c.children); 

如果您有类似下面的课程

 public class Node { public string Name; public List Children = new List(); } 

你的linq会

  Func, IEnumerable> Flatten = null; Flatten = coll => coll.SelectMany(n=>n.Concat(Flatten(n.Children))); 

测试代码:

 Node[] roots = new Node[]{ new Node(){Name="A"},new Node(){Name="B"},new Node(){Name="C"} }; roots[0].Children.Add(new Node(){Name="D"}); roots[0].Children.Add(new Node(){Name="E"}); roots[0].Children.Add(new Node(){Name="F"}); roots[1].Children.Add(new Node(){Name="G"}); roots[2].Children.Add(new Node(){Name="H"}); roots[2].Children.Add(new Node(){Name="I"}); Func, IEnumerable> Flatten = null; Flatten = coll => coll.SelectMany(n=>n.Concat(Flatten(n.Children))); var result = String.Join(",",Flatten(roots).Select(x=>x.Name)); Console.WriteLine(result); 

由于您只有两个级别,因此以下方法应该有效:

 var result = (from parent in masternodes select new Node[] { parent }.Concat(parent.children)).SelectMany(i => i); 

首先,它创建父项及其子项的可枚举:

 [A, D, E, F] [B, G] [C, H] 

然后用SelectMany将它们SelectMany

比如说,我们有以下几个类:

 public class MasterNode : ChildNode { public List ChildNodes; } public class ChildNode { public string Value; } 

然后

  List list = new List { new MasterNode { Value="A", ChildNodes = new List { new ChildNode{Value = "D"}, new ChildNode{Value = "E"}, new ChildNode{Value = "F"} } }, new MasterNode { Value="B", ChildNodes = new List { new ChildNode{Value = "G"} } }, new MasterNode { Value="C", ChildNodes = new List { new ChildNode{Value = "H"}, new ChildNode{Value = "I"} } } }; foreach (ChildNode c in list.SelectMany(l => { List result = l.ChildNodes.ToList(); result.Insert(0, l); return result; })) { Console.WriteLine(c.Value); }