Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用LINQ按深度级别对对象层次进行排序?_C#_Linq_Sorting_Hierarchy_Hierarchical Data - Fatal编程技术网

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语句:

  • 在此基础上运行聚合,计算并存储级别
  • 对按级别排序的结果运行第二个LINQ查询

  • B当然很简单。这是一个我正在努力解决的问题。我当然可以按程序来做,但我认为这可以简化为LINQ语句。

    您没有指定查询的目标,因此有多个正确答案:

  • LINQ to SQL或LINQ to Entities-不支持递归查询,因此您必须将数据加载到内存中并执行LINQ to Objects查询,或者在数据库中使用存储过程(最有可能使用公共表表达式)。您还可以在数据库中准备一个视图并将其映射到EF模型

  • LINQ to Objects更适合这项工作,但在我看来,使用一种计算深度的简单方法仍然是最好的:

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