c# - LINQ sort a flat list based on childorder -
i trying figure out way sort elements linq , c#, kinda failing so.
for problem let assume have following table
---temptable id (int) parentid (int) name (varchar) sortorder (int)
the id , parentid related each other , give me self hierachical data structure. root elements have null in id field. sortorder portion of whole table , based on parentid, elements share same parentid have 1, 2, 3 in it.
lets further assume 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 following order:
test 1 //root element sort order 1 = top test 2 //child element of root sort order 1 test 4 //child element of test 2 sort order 1 test 3 //child element of root sort order 2
also object without getting portion of information threw usage of select new ...
this 1 of failed tries:
from x in entitymodel.temptables //dbset<temptable> entityframework - holds elements orderby x.sortorder y in x.temptablechildren //navigation property entityframework orderby y.sortorder select y
thanks in advance help.
edit:
the order parentid maybe helpfull, given testdata since id, parentids in perfect order isnt case in real live application since data driven, delete entry create new 1 , place in order under parent , have :
id = 193475037 parentid = 2 name = test 192375937 sortorder = 25
now in application possible move 1 , parentid , sortorder change randomly like:
id = 193475037 parentid = 456798424 name = test 192375937 sortorder = 4
to furhter explain problem here code - how without 1 beautifull linq query 2 , 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 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 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>(); (int = 0; < 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 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 reading desired result depth-first traversal, elements @ same level depth should ordered 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 similar method smaller & recursive. , works many depth levels. prefer calling tolist because close resultset before querying next query.
there no way in single query of now.
with single query requested
entity framework automatically fill 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 automatically fill each children on fetch // need top level nodes // pass preparelist method var list = context.temptables.tolist().where(x=> x.parentid == null); var sortedlist = preparelist(list).tolist(); // create list @ end if going // iterate many times , logic not change.
Comments
Post a Comment