考虑这个层次结构
A
|
--------------------------
| |
B C
| |
--------|-------- ---------
| | | | |
D E F G H
| | | |
| --------- ------- |
| | | | | |
I J K L M N
每个对象都有一个Parent属性和一个choldren的Items集合,因此,例如,E的父节点为B,N为H,等等。对于父节点,A的值为null。 B.Items包含D-F等。
什么是LINQ语句,我可以根据它们的级别对它们进行排序?我并不关心一个级别内的排序顺序(即D-H的顺序并不重要,但它们必须在B和C之后,它们必须在A之后。
我能想到的只有两个独立的linq语句:
B当然很容易。这是我挣扎的A。我当然可以在程序上做到这一点,但我必须认为这可以简化为LINQ语句。
答案 0 :(得分:7)
您没有指定查询的目标,因此有多个正确的答案:
LINQ to SQL或LINQ to Entities - 不存在对递归查询的支持,因此您必须将数据加载到内存中并执行LINQ to Objects查询,或者在数据库中使用存储过程(最有可能使用Common Table Expression)。您还可以在数据库中准备VIEW并将其映射到您的EF模型。
LINQ to Objects更适合这项工作,但IMO你最好用一种计算深度的简单方法:
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查询会很容易,并且Parent有所有子项的链接。在这种情况下,查询数据更容易,因为每个项目都有一组依赖项,而LINQ适用于集合,对象是单个项目,例如Parent
属性。我刚才写了一篇关于querying hierarchy of objects using LINQ的博客文章,你可能会觉得它很有趣。
答案 1 :(得分:2)
虽然我的朋友们已经发布了一些好的答案,但我还是愿意提供另一个答案。如果更改节点结构以获得更好的性能是可行的,则可以为每个节点设置一次深度属性,然后根据depth属性对它们进行排序。
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);
在这里它在sort方法中调用,或者你选择任何其他地方来做。
答案 2 :(得分:0)
有几种方法可以解决这个问题。
首先,你可以实现方法,f.e。 AsFlattenEnumerable
。在映像中,您拥有类Tree<T>
的类T
的类Flatten<T>
。
声明课程public Flatten<T>
{
public T Data { get; }
public int Level { get; }
}
:
AsFlattenEnumerable
然后实施public class Tree<T>
{
. . .
public IEnumerable<Flatten<T>> AsFlattenEnumerable()
{
. . .
}
. . .
}
:
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 };
现在您可以使用简单的LINQ查询对节点进行排序:
order by
其次,C#编译器允许您为OrderBy
子句(以及Tree<T>
方法)声明自己的方法。
成像,你有一个实现ITreeEnumerable<T>
接口的System.Web
类(在Where
程序集中有类似的分层接口)。
然后,您可以定义自己的扩展方法,例如OrderBy
和public static ITreeEnumerable<TSource> OrderBy<TSource, TKey>(this ITreeEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
. . .
}
:
var orderedNodes = from node in tree // tree implements ITreeEnumerable<T>
// First Level (by default), then custom ordering by A and B
order by new { node.A, node.B };
. . .
var enumerableNodes = orderedNodes.AsEnumerable();
现在您可以使用LINQ对
var result = from i in obj
order i by i.Foo;
C#编译器转换此表达式:
var result = obj.OrderBy(i => i.Key);
到此:
obj
IEnumerable
变量可以是任何类型,不一定是obj
。 OrderBy
的类型应该有<button class="btn btn-default" data-dismiss="modal" id="btndialog" >DIALOG</button>
方法,或者您可以实现扩展方法。
答案 3 :(得分:0)
无耻的插件 - 您可以添加我一直在使用的名为Treenumerable的Nuget包:
您必须实现一个知道如何遍历树的ITreeWalker接口(两种方法)。一旦你这样做,你就可以这样做:
// Pseudocode
TreeWalker walker = new TreeWalker();
IEnumerable<YourType> levelOrder = walker.LevelOrderTraversal(A);
您还可以获得十几个(如果计算重载次数更多)或其他枚举/查询树结构的方法。当前版本 - 1.2.0 - 具有良好的测试覆盖率,并且似乎非常稳定。