Here is an extended version of your HierarchyTable
class that gets you pretty close to what you're looking for. Basically it just adds the following.
- a
ToString() method
- a static
HierarchicalSort()
method that sorts ICollection<HierarchyTable>
.
- a static
GetNextNode()
method that that is used in the creating the HierarchicalSort.
The HierarchicalSort
sort method creates both the hierarchy and the sort but only returns the sort. The hierarchy is just left behind so splitting it into two methods might be a good idea. When the function returns, the topNode
variable contains the hierarchical data.
There was no real reason to use a custom IComparer because you're only really sorting on SortOrder
in your example. You could easily extend it if you need to though.
public class HierarchyTable {
public HierarchyTable(HierarchyTable parent) {
Parent = parent;
Children = new List<HierarchyTable>();
}
public int ID { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
public int ParentID { get; set; }
//Navigation Properties created by Entity Framework
public virtual HierarchyTable Parent { get; set; }
public virtual ICollection<HierarchyTable> Children { get; set; }
public override string ToString() {
StringBuilder sb = new StringBuilder();
sb.Append("ID: " + ID).Append('\t');
sb.Append("Name: " + Name).Append('\t');
sb.Append("SortOrder: " + SortOrder).Append('\t');
sb.Append("ParentID: " + ParentID);
return sb.ToString();
}
//-----------------------------------------------------
// Builds a hierarchical tree out of a List<HierarchyTable>
// and copies each child row into a different
// List<HierarchyTable> as it is being built.
//-----------------------------------------------------
public static List<HierarchyTable> HierarchicalSort(ICollection<HierarchyTable> inputlist) {
HierarchyTable topNode = inputlist.ElementAt(0);
HierarchyTable current = topNode;
HierarchyTable child = null;
inputlist.Remove(topNode);
List<HierarchyTable> copyList = inputlist.Take(inputlist.Count).ToList();
List<HierarchyTable> outputList = new List<HierarchyTable>() { topNode };
foreach (HierarchyTable rec in inputlist) {
do {
child = GetNextNode(current, copyList);
if (child != null) {
child.Parent = current;
current.Children.Add(child);
current = child;
copyList.Remove(child);
outputList.Add(child);
}
} while (child != null);
current = topNode;
}
return outputList;
}
//-----------------------------------------------------
// Returns the first child of a sorted match on
// ID == ParentID. If you end up needing to sort on
// multiple columns or objects that don't already
// implement IComparer, then make your own comparer
// class. The syntax would be:
//
// .OrderBy(x => x, new ComparerClass());
//-----------------------------------------------------
private static HierarchyTable GetNextNode(HierarchyTable current, ICollection<HierarchyTable> inputlist) {
List<HierarchyTable> sublist = inputlist
.Where(
a => a.ParentID == current.ID
).OrderBy(
x => x.SortOrder
).ToList();
if(sublist.Count > 0)
return sublist.First();
return null;
}
}
Sample Usage:
List<HierarchyTable> htable = new List<HierarchyTable>(){
new HierarchyTable() {ID = 1, Name = "A", SortOrder = 0, ParentID = 0},
new HierarchyTable() {ID = 2, Name = "B", SortOrder = 1, ParentID = 4},
new HierarchyTable() {ID = 3, Name = "C", SortOrder = 2, ParentID = 1},
new HierarchyTable() {ID = 4, Name = "D", SortOrder = 1, ParentID = 1},
new HierarchyTable() {ID = 5, Name = "E", SortOrder = 1, ParentID = 3}
};
List<HierarchyTable> sorted = HierarchyTable.HierarchicalSort(htable);
foreach (HierarchyTable ht in sorted)
Console.WriteLine(ht);
Console:
ID: 1 Name: A SortOrder: 0 ParentID: 0
ID: 4 Name: D SortOrder: 1 ParentID: 1
ID: 2 Name: B SortOrder: 1 ParentID: 4
ID: 3 Name: C SortOrder: 2 ParentID: 1
ID: 5 Name: E SortOrder: 1 ParentID: 3
Good luck. I hope this helps.
UPDATE
Another approach would be to go ahead and flatten the hierarchy into a list and then call the sort method using an IComparer similar to this.
public class HierarchyTableComparer : IComparer<HierarchyTable> {
public int Compare(HierarchyTable a, HierarchyTable b) {
int comp = 0;
if ((comp = a.SortOrder.CompareTo(b.SortOrder)) != 0) {
return comp;
}
else if ((comp = a.ID.CompareTo(b.ParentID)) != 0) {
return comp;
}
else {
return 0;
}
}
}
If the framework does the initial job of linking the hierarchical elements, then traditional sorting works because the list will already be sorted by levels.
Sample Usage :
//----------------------------------------------------
// Flattened Hierarchy
//----------------------------------------------------
List<HierarchyTable> htable = new List<HierarchyTable>(){
new HierarchyTable() {ID = 1, Name = "A", SortOrder = 0, ParentID = 0},
new HierarchyTable() {ID = 4, Name = "D", SortOrder = 1, ParentID = 1},
new HierarchyTable() {ID = 2, Name = "B", SortOrder = 1, ParentID = 4},
new HierarchyTable() {ID = 3, Name = "C", SortOrder = 2, ParentID = 1},
new HierarchyTable() {ID = 5, Name = "E", SortOrder = 1, ParentID = 3}
};
htable.Sort(new HierarchyTableComparer());
foreach (HierarchyTable ht in htable)
Console.WriteLine(ht);
Console:
ID: 1 Name: A SortOrder: 0 ParentID: 0
ID: 4 Name: D SortOrder: 1 ParentID: 1
ID: 2 Name: B SortOrder: 1 ParentID: 4
ID: 5 Name: E SortOrder: 1 ParentID: 3
ID: 3 Name: C SortOrder: 2 ParentID: 1