LINQ基于childorder排序的平面列表 [英] LINQ sort a flat list based on childorder

查看:127
本文介绍了LINQ基于childorder排序的平面列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在试图找出我的LINQ和C#的元素进行排序的好方法,但我还挺不这样做。

有关问题让假设你有如下表

  ---不是Temptable
ID(INT)
PARENTID(INT)
名称(VARCHAR)
SortOrder的(INT)

的ID和PARENTID都彼此相关,并给我一个自层次式数据结构。
根元素在ID字段为空。
该SortOrder的是仅整个表的一部分,并且基于所述PARENTID,以便共享相同PARENTID元件做有1个,2个,3个在它

让我们进一步假设以下数据:

  ID = 1
PARENTID = NULL
名称=测试1
SortOrder的= 1ID = 2
PARENTID = 1
名称=测试2
SortOrder的= 1ID = 3时
PARENTID = 1
名称=测试3
SortOrder的= 2ID = 4时
PARENTID = 2
名称=测试4
SortOrder的= 1

我的期望平面列表应具有以下命令:

 与排序1 =最顶端测试1 //根元素
根与排序顺序1测试2 //子元素
测试2与排序顺序1测试4 //子元素
根与排序顺序2测试3 //子元素

另外,我喜欢让对象本身,而不只得到的信息的一部分扔的使用选择新...

这是我失败的尝试之一:

 从EntityModel.TempTables // DbSet&LT X;不是Temptable>通过的EntityFramework  - 这已经持有的所有元素
   排序依据x.SortOrder
   从在的EntityFramework // x.TempTableChildren导航性能Y
   排序依据y.SortOrder
   选择Y

在此先感谢您的帮助。

编辑:

与PARENTID的顺序可能有帮助,有自ID给定TESTDATA,ParentIDs都井井有条,但这个心不是这样的,因为它的数据驱动的一个个活生生的应用程序,有人可能会删除的条目创建一个新的和地点它在父下,按一定的顺序,你会碰到这样的:

  ID = 193475037
PARENTID = 2
名称=测试192375937
SortOrder的= 25

现在在应用程序将有可能推动这一之一和PARENTID和SortOrder的随机会更改为类似:

  ID = 193475037
PARENTID = 456798424
名称=测试192375937
SortOrder的= 4

要furhter解释这里的问题是一些code - 我会怎么做,而不1美丽的LINQ查询但2和一些产量回报:

 公共类LinqTestDemo
{
    随机兰特=新的随机();
    清单<不是Temptable>名单=新名单,LT;不是Temptable>();    公开名单<不是Temptable> GetFlatData()
    {
        清单= GetTestData();        VAR rootElement的=(从列表中移除x
                            其中,x.ParentID == NULL
                            排序依据x.SortOrder
                            选择X).ToList();        变种flatList = OrderChilds(rootElement的).ToList();        的foreach(在flatList VAR不是Temptable)
        {
            Console.WriteLine(的String.Format(ID = {0} - PARENTID = {1} - 名称= {2} - SortOrder的= {3},tempTable.ID,tempTable.ParentID,tempTable.Name,tempTable.SortOrder) );
        }        返回flatList;
    }    私人的IEnumerable<不是Temptable> OrderChilds(列表<不是Temptable>枚举)
    {
        的foreach(在枚举VAR不是Temptable)
        {
            产生的回报不是Temptable;            不是Temptable表=不是Temptable;
            VAR蔡尔兹= OrderChilds((从列表中移除x
                                        其中,x.ParentID == table.ID
                                        排序依据x.SortOrder
                                        选择X).ToList());            的foreach(VAR孩子童车)
            {
                产生收益的孩子;
            }
        }
    }    公开名单<不是Temptable> GetTestData()
    {
        VAR的returnValue =新的List<不是Temptable>();
        的for(int i = 0; I< 50;我++)
        {
            VAR不是Temptable =新不是Temptable();
            tempTable.ID = I;
            如果(我== 0)
                tempTable.ParentID = NULL;
            其他
                tempTable.ParentID = rand.Next(0,I);            变种maxSortOrder =(从的returnValue点¯x
                                其中,x.ParentID == tempTable.ParentID
                                选择(INT?)x.SortOrder)。最大();            如果(maxSortOrder.HasValue)
                tempTable.SortOrder = maxSortOrder.Value + 1;
            其他
                tempTable.SortOrder = 1;            tempTable.Name =的String.Format({测试} 0点00分,我);
            returnValue.Add(不是Temptable);
        }        返回的returnValue;
    }    公共类不是Temptable
    {
        公众诠释ID {搞定;组; }
        公众诠释? PARENTID {搞定;组; }
        公共字符串名称{;组; }
        公众诠释SortOrder的{搞定;组; }
    }
}

@广度优先VS深度优先遍历:
一些阅读后,我会说我想要的结果将是深度优先遍历,其中在同级别深度的元素应该由物业SortOrder的订购。


解决方案

 公共lEnumerable<不是Temptable>的GetList(INT?=的parentID NULL){     在Context.TempTables的foreach(VAR项目
        。凡(X => x.ParentID ==的parentID)
        .OrderBy(X => x.SortOrder)
        .ToList(){        产生回报的项目;        的foreach(VAR孩子的GetList(item.ID))
        {
            产生收益的孩子;
        }     }
  }
  VAR排序列表=的GetList();

这是一个类似的方法,但它是小&安培;递归的。而工作在很多深层次。我preFER调用了ToList,因为它会查询一个查询之前关闭ResultSet。

有没有办法在单个查询截至目前做到这一点。

随着单个查询的要求

实体框架会自动补全的孩子。

 公开的IEnumerable<不是Temptable> prepareList(IEnumerable的<不是Temptable>名单){
     清单= list.OrderBy(X => x.SortOrder);
     的foreach(在列表VAR项){
         产生回报的项目;
         的foreach(VAR孩子prepareList(item.ChildTempTables)){
             产生收益的孩子;
         }
     }
 } //因为EF会自动填补提取每个孩子
 //我们需要的仅仅是一个顶级节点
 //我们将传递到prepareList方法
 VAR列表= Context.TempTables.ToList()式(X => x.ParentID == NULL);
 VAR排序列表= prepareList(名单).ToList(); //这是件好事末创建列表,如果你要
 //迭代它多次和逻辑不会改变。

I am currently trying to figure out a good way to sort my elements with LINQ and C#, but I am kinda failing to do so.

For the problem let assume you have the following Table

---TempTable
ID (int)
ParentID (int)
Name (varchar)
SortOrder (int)

The ID and ParentID are related to each other and give me a self hierachical data structure. The root elements have a null in the ID Field. The SortOrder is only a portion of the whole table and based on the ParentID, so the elements that share the same ParentID do have 1, 2, 3 in it.

Lets further assume the following data:

ID = 1
ParentID = null
Name = Test 1
SortOrder = 1

ID = 2
ParentID = 1
Name = Test 2
SortOrder = 1

ID = 3
ParentID = 1
Name = Test 3
SortOrder = 2

ID = 4
ParentID = 2
Name = Test 4
SortOrder = 1

My desired flat list should have the following order:

Test 1 //root element with sort order 1 = very top
Test 2 //child element of root with sort order 1
Test 4 //child element of test 2 with sort order 1
Test 3 //child element of root with sort order 2

Also I like to get the object itself without only getting a portion of information threw the usage of select new ...

This is one of my failed tries:

from x in EntityModel.TempTables //DbSet<TempTable> by EntityFramework - which already holds all elements
   orderby x.SortOrder
   from y in x.TempTableChildren //Navigation Property by EntityFramework
   orderby y.SortOrder
   select y

Thanks in advance for your help.

Edit:

The order with the ParentID maybe helpfull, with the given TestData since the ID, ParentIDs are in perfect order but this isnt the case in a real live application since its data driven, someone could delete a entry create a new one and place it in a certain order under a parent and you would have something like :

ID = 193475037
ParentID = 2
Name = Test 192375937
SortOrder = 25

Now in the application it would be possible to move this one and the ParentID and SortOrder would change randomly to something like:

ID = 193475037
ParentID = 456798424
Name = Test 192375937
SortOrder = 4

To furhter explain the problem here is some code - how I would do it without 1 beautifull Linq Query but with 2 and some yield return:

public class LinqTestDemo
{
    Random rand = new Random();
    List<TempTable> list = new List<TempTable>();

    public List<TempTable> GetFlatData()
    {
        list = GetTestData();

        var rootElement = (from x in list
                            where x.ParentID == null
                            orderby x.SortOrder
                            select x).ToList();

        var flatList = OrderChilds(rootElement).ToList();

        foreach (var tempTable in flatList)
        {
            Console.WriteLine(string.Format("ID = {0} - ParentID = {1} - Name = {2} - SortOrder = {3}", tempTable.ID, tempTable.ParentID, tempTable.Name, tempTable.SortOrder));
        }

        return flatList;
    }

    private IEnumerable<TempTable> OrderChilds(List<TempTable> enumerable)
    {
        foreach (var tempTable in enumerable)
        {
            yield return tempTable;

            TempTable table = tempTable;
            var childs = OrderChilds((from x in list
                                        where x.ParentID == table.ID
                                        orderby x.SortOrder
                                        select x).ToList());

            foreach (var child in childs)
            {
                yield return child;
            }
        }
    }

    public List<TempTable> GetTestData()
    {
        var returnValue = new List<TempTable>();
        for (int i = 0; i < 50; i++)
        {
            var tempTable = new TempTable();
            tempTable.ID = i;
            if (i == 0)
                tempTable.ParentID = null;
            else
                tempTable.ParentID = rand.Next(0, i);

            var maxSortOrder = (from x in returnValue
                                where x.ParentID == tempTable.ParentID
                                select (int?)x.SortOrder).Max();

            if (maxSortOrder.HasValue)
                tempTable.SortOrder = maxSortOrder.Value + 1;
            else
                tempTable.SortOrder = 1;

            tempTable.Name = string.Format("Test {0:00}", i);
            returnValue.Add(tempTable);
        }

        return returnValue;
    }

    public class TempTable
    {
        public int ID { get; set; }
        public int? ParentID { get; set; }
        public string Name { get; set; }
        public int SortOrder { get; set; }
    }
}

@ Breadth-First vs Depth-First Traversal: After some reading I would say my desired result would be Depth-First Traversal, where the elements at the same level depth should be ordered by the property SortOrder.

解决方案

  public lEnumerable<TempTable> GetList( int? parentID = null){

     foreach ( var item in Context.TempTables
        .Where( x => x.ParentID == parentID )
        .OrderBy( x=> x.SortOrder)
        .ToList() {

        yield return item;

        foreach( var child in GetList( item.ID))
        {
            yield return child;
        }

     }
  }


  var sortedList = GetList();

It is similar to your method but it is smaller & recursive. And works for many depth levels. I prefer calling ToList because it will close resultset before querying next query.

There is no way to do this in single query as of now.

With Single Query as Requested

Entity Framework will automatically fill all children.

 public IEnumerable<TempTable> PrepareList(IEnumerable<TempTable> list){
     list = list.OrderBy( x=> x.SortOrder);
     foreach(var item in list){
         yield return item;
         foreach(var child in PrepareList(item.ChildTempTables)){
             yield return child;
         }
     }
 }

 // since EF will automatically fill each children on fetch
 // all we need is just a top level nodes
 // which we will pass to PrepareList method
 var list = Context.TempTables.ToList().Where(x=> x.ParentID == null);
 var sortedList = PrepareList(list).ToList();

 // it is good to create list at the end if you are going to 
 // iterate it many times and logic will not change.

这篇关于LINQ基于childorder排序的平面列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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