LINQ sort a flat list based on childorder

£可爱£侵袭症+ 提交于 2019-11-28 09:44:05
  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.

Here's a non-recursive version. It won't iterate again and again over the initial list. Instead it maintains a dictionary for the parent-to-children relationship and stores the current position of the ongoing pre-order tree traversal in enumerators.

public static IEnumerable<TempTable> PreorderForest(IEnumerable<TempTable> list)
{
    var nodesByParent = list.GroupBy(x => x.ParentID.GetValueOrDefault(-1))
        .ToDictionary(xs => xs.Key, 
                      xs => xs.OrderBy(x => x.SortOrder).GetEnumerator());

    var stack = new Stack<IEnumerator<TempTable>>();
    stack.Push(nodesByParent[-1]);

    while (stack.Count > 0)
    {
        var nodes = stack.Peek();
        if (nodes.MoveNext())
        {
            yield return nodes.Current;
            IEnumerator<TempTable> children;
            if (nodesByParent.TryGetValue(nodes.Current.ID, out children))
                stack.Push(children);
        }
        else
            stack.Pop();
    }
}

Actually I don't know if it could be made by elegant LINQ query. Here's recursive version of DFS, it builds lookup to speed up search by ParentID

public static IEnumerable<TempTable> SortedList(IEnumerable<TempTable> list = null, int? ParentID = null, ILookup<int?, TempTable> lookup = null)
{
    if (lookup == null)
        lookup = list.ToLookup(x => x.ParentID, x => x);

    foreach (var p in lookup[ParentID].OrderBy(x => x.SortOrder))
    {
        yield return p;
        foreach (var c in SortedList(lookup: lookup, ParentID: p.ID))
            yield return c;
    }
}

Try this:

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

public void DoWork()
    {
        Item[] data = new Item[] {
            new Item() { ID = 2, ParentID = 1, Name = "Test 2", SortOrder = 1},
            new Item() { ID = 3, ParentID = 1, Name = "Test 3", SortOrder = 2},
            new Item() { ID = 4, ParentID = 2, Name = "Test 4", SortOrder = 1},
            new Item() { ID = 1, ParentID = null, Name = "Test 1", SortOrder = 1},
        };

        var result = from x in data
                     orderby x.SortOrder, x.ParentID
                     select x;

        foreach (var row in result.ToArray())
        {
            Console.WriteLine(row.Name);
        }
    }

I guess it's all about the proper ordering

Here's a simple solution:

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

public List<TempTable> GetTempData()
{
    var temp = new List<TempTable>();
    temp.Add(new TempTable { ID = 1, ParentID = null, Name = "Test 1", SortOrder = 1 });
    temp.Add(new TempTable { ID = 2, ParentID = 1, Name = "Test 2", SortOrder = 1 });
    temp.Add(new TempTable { ID = 3, ParentID = 1, Name = "Test 3", SortOrder = 3 });
    temp.Add(new TempTable { ID = 4, ParentID = 2, Name = "Test 4", SortOrder = 1 });
    temp.Add(new TempTable { ID = 5, ParentID = 1, Name = "Test 5", SortOrder = 2 });
    return temp;
}

Usage:

var data = GetTempData();
var result = data.OrderBy(d => d.SortOrder).ThenBy(d => d.ParentID);
//Do something with result
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!