C# 如何使用LINQ按深度级别对对象层次进行排序?
考虑一下这个层次结构C# 如何使用LINQ按深度级别对对象层次进行排序?,c#,linq,sorting,hierarchy,hierarchical-data,C#,Linq,Sorting,Hierarchy,Hierarchical Data,考虑一下这个层次结构 A | -------------------------- | | B C | | --------|-------- --------- | |
A
|
--------------------------
| |
B C
| |
--------|-------- ---------
| | | | |
D E F G H
| | | |
| --------- ------- |
| | | | | |
I J K L M N
每个对象都有一个父属性和一个choldren的Items集合,例如,E有一个B的父对象,N有一个H的父对象,等等。a有一个null值作为父对象。B.项目包含D-F等
什么是LINQ语句,我可以按级别对它们进行排序?我不关心级别内的排序顺序(即D-H的顺序不重要,但它们必须在B和C之后,而B和C必须在a之后)
我唯一能想到的是两个独立的linq语句:
B当然很简单。这是一个我正在努力解决的问题。我当然可以按程序来做,但我认为这可以简化为LINQ语句。您没有指定查询的目标,因此有多个正确答案:
public static int GetDepth(Item item)
{
int d = 0;
while ((item = item.Parent) != null)
d++;
return d;
}
以后的查询非常简单
var results = from item in data
let depth = GetDepth(item)
orderby depth descending
select item;
如果您的数据结构不同,并且父对象具有指向所有子对象的链接,那么只编写一个LINQ to Objects查询就很容易了。在这种情况下,查询数据就更容易了,因为每个项目都有一个依赖项的集合,LINQ与集合配合得很好,可以访问单个项目,例如您的父对象属性。我写道如果你不久前写了一篇博文,你可能会觉得很有趣
有几种方法可以解决这个问题 首先,您可以实现方法,例如,
asplantenumerable
。Imaging,您有一个类Tree
,它具有T
类型的节点数据
声明类展平:
现在,您可以使用简单的LINQ查询对节点进行排序:
var ordereNodes = from node in tree.AsFlattenEnumerable()
// F.e. first Level, then A, then B
order node by new { node.Level, node.Data.A, node.Data.B };
其次,C#编译器允许您为OrderBy
子句声明自己的方法(当然还有OrderBy
方法)
在图像处理中,您有一个树
类,该类实现了一个ITreeEnumerable
接口(在System.Web
assembly中有类似的分层接口)
然后您可以定义自己的扩展方法,如Where
和OrderBy
:
public static ITreeEnumerable<TSource> OrderBy<TSource, TKey>(this ITreeEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
. . .
}
为此:
var result = obj.OrderBy(i => i.Key);
obj
变量可以是任何类型的,不一定是IEnumerable
。obj
的类型应该有OrderBy
方法,或者你可以实现扩展方法。虽然我的朋友已经给出了一些很好的答案,但我愿意提供另一个答案。如果改变b的节点结构可行的话为了获得更好的性能,您可以为每个节点设置一次深度属性,然后根据深度属性对其进行排序
public class Node
{
public Node()
{
this.Items = new List<Node>();
this.Parent = null;
this.Depth = 0;
}
public Node Parent { get; set; }
public List<Node> Items { get; set; }
public int Depth { get; set; }
}
public void SetDepths(Node node, int depth)
{
node.Depth = depth;
foreach (var child in node.Items)
SetDepths(child, depth + 1);
}
public void Sort(List<Node> nodes)
{
SetDepths(nodes.Single(x => x.Parent == null), 1);
nodes = nodes.OrderBy(x => x.Depth).ToList();
}
这里它是在排序方法中调用的,或者您可以选择任何其他位置来执行此操作。无耻的插件-您可以添加我一直在使用的名为Treenumerable的Nuget包:
您必须实现一个ITreeWalker接口,该接口知道如何遍历您的树(两种方法)。一旦这样做,您就可以简单地执行以下操作:
// Pseudocode
TreeWalker walker = new TreeWalker();
IEnumerable<YourType> levelOrder = walker.LevelOrderTraversal(A);
//伪代码
TreeWalker=新TreeWalker();
IEnumerable levelOrder=walker.LevelOrderTraversal(A);
你也会得到一打(如果你算上超载的话会更多)或者其他枚举/查询树结构的方法。当前版本-1.2.0-具有良好的测试覆盖率,并且似乎相当稳定。LINQ到什么?LINQ到SQL?LINQ到实体?LINQ到对象?我认为您可以使用存储过程来实现性能。如果家长知道孩子?@Sven,是的,家长也通过I知道他们的孩子tems property.Re如果您的数据结构不同,您的博客文章也不同,那么只需编写一个LINQ to Objects查询就很容易了-如果您包含“编写您自己的扩展方法”,则只需编写一个LINQ to Objects查询就可以了在这一点上…项目也知道它们的子项。每个项目都有一个父项和一个称为项目的子项集合。当一个人指定LINQ,而不是LINQ to xxx时,你应该始终假设他们只是指对象上的System.LINQ。:)这是一个很好的解决方案,但如果你只对图形进行一次BFS,将每个节点连接到它的深度,然后使用一个简单的OrderBy(p=>p.Depth)
。图形不断变化。一直在添加和删除节点。问题是节点可能出现无序,所以我需要这个函数。我可以保证先添加较低的节点。我一定会查看此信息!:)
var result = from i in obj
order i by i.Foo;
var result = obj.OrderBy(i => i.Key);
public class Node
{
public Node()
{
this.Items = new List<Node>();
this.Parent = null;
this.Depth = 0;
}
public Node Parent { get; set; }
public List<Node> Items { get; set; }
public int Depth { get; set; }
}
public void SetDepths(Node node, int depth)
{
node.Depth = depth;
foreach (var child in node.Items)
SetDepths(child, depth + 1);
}
public void Sort(List<Node> nodes)
{
SetDepths(nodes.Single(x => x.Parent == null), 1);
nodes = nodes.OrderBy(x => x.Depth).ToList();
}
SetDepths(rootNode, 1);
// Pseudocode
TreeWalker walker = new TreeWalker();
IEnumerable<YourType> levelOrder = walker.LevelOrderTraversal(A);