我目前正在开发一个IComparer,它对于int和string的简单属性工作正常,而且asending和descending也正常工作,但是我遇到了一个层次结构的数据结构问题。
让我们假设您的数据库中包含以下表格:
HierarchyTable
ID, int
Name, string
SortOrder, int
ParentID, int
HierarchyTable具有ID和ParentID之间的关系,以建立自引用关系,构建我们的层次结构。
现在问题从我的SortOrder开始。 SortOrder不是一个唯一的int,它表示整个级别的排序顺序,而只存储您当前级别的排序顺序。
让我们假设以下数据:
ID --- Name --- SortOrder --- ParentID
1 --- A --- 0 --- null
2 --- B --- 1 --- 4
3 --- C --- 2 --- 1
4 --- D --- 1 --- 1
5 --- E --- 1 --- 3
这将导致以下层次结构:
ID --- Name --- SortOrder --- ParentID
1 --- A --- 0 --- null
4 --- D --- 1 --- 1
2 --- B --- 1 --- 4
3 --- C --- 2 --- 1
5 --- E --- 1 --- 3
现在我希望将这个层次结构放在一个平面列表中,借助IComparer和一个只调用Sort方法的List,这里是一个正确的排序平面列表。
此表格结构位于我的实体框架应用程序中,代表实体之一,因此如果需要,我可以将其扩展为其他属性。
这个简单示例的实体看起来像这样:
public class HierarchyTable
{
public int ID { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
public in ParentID { get; set; }
//Navigation Properties created by Entity Framework
public virtual HierarchyTable Parent { get; set; }
public virtual ICollection<HierarchyTable> Children { get; set; }
}
答案 0 :(得分:1)
您的比较器需要SortOrders的列表,遵循每条记录(父,子,孙......)的整个祖先链。像这样:
ID --- Name --- SortOrder --- ParentID --- HierarchicalSortOrder
1 --- A --- 0 --- null --- 0
2 --- B --- 1 --- 4 --- 0,1,1
3 --- C --- 2 --- 1 --- 0,2
4 --- D --- 1 --- 1 --- 0,1
5 --- E --- 1 --- 3 --- 0,2,1
然后你可以简单地对HierarchicalSortOrder进行排序:
1 --- 0
4 --- 0,1
2 --- 0,1,1
3 --- 0,2
5 --- 0,2,1
以下函数构造此HierarchicalSortOrder:
public string GetHierarchicalSortOrder(HierarchyTable element)
{
List<int> sortOrders = new List<int>() {element.SortOrder};
while (element.Parent != null)
{
element = element.Parent;
sortOrders.Insert(0, element);
}
return String.Join(",", sortOrders);
}
为简单起见,我假设排序顺序没有联系;如果有,你还应该在列表中包含element.ID,否则孩子会被附加到错误的父母身上。
答案 1 :(得分:1)
首先应使用级别编号展平结构,然后按levelNo + SeqNo排序
查看我上一个问题的答案:
Flatten (with level numbers) a hierarchical list
答案 2 :(得分:1)
以下是HierarchyTable
课程的扩展版本,可让您非常接近您所寻找的内容。基本上它只是增加了以下内容。
ToString() method
HierarchicalSort()
进行排序的静态ICollection<HierarchyTable>
方法。GetNextNode()
方法。 HierarchicalSort
排序方法同时创建层次结构和排序,但只返回排序。层次结构刚刚落后,因此将其拆分为两种方法可能是一个好主意。函数返回时,topNode
变量包含分层数据。
没有真正的理由使用自定义IComparer,因为您只是在示例中对SortOrder
进行排序。如果需要,您可以轻松扩展它。
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;
}
}
样本用法:
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);
控制台:
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
祝你好运。我希望这会有所帮助。
<强>更新强>
另一种方法是继续将层次结构展平为列表,然后使用类似于此的IComparer调用sort方法。
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;
}
}
}
如果框架完成了链接层次元素的初始工作,那么传统的排序就可以了,因为列表已经按级别排序了。
样本用法:
//----------------------------------------------------
// 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);
控制台:
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
答案 3 :(得分:1)
如何在HierarchyTable类中使用递归排序。这通常是我在抽象语法树中导航的方式。如果您计划使用HierarchyTable执行各种操作,并且希望对类进行最少的修改,那么使用Visitor设计模式可能是您的最佳选择。它是处理分层节点结构时使用的事实模式。一旦实现,它允许您将依赖于遍历的函数添加到树节点,而根本不更改节点的类。
如果您尚未使用它,我很乐意展示它是如何实现的,只要问一下。我再也不想为你的问题走错路。 :)
如果您有兴趣,这些wiki可以很好地概述访问者模式的使用情况:
基本上,当您想要询问每个节点时,标准模式是最佳的,当您想要快速短路某些节点而不是浪费时间时,最好使用分层模式。他们都在分层数据上工作,所以我真的不喜欢命名区别。
无论如何,这里是使用IComparer在HierarchyTable类中进行的递归排序。使用这些技术还可以从层次结构中的任何级别开始排序。
public List<HierarchyTable> FlatSort(IComparer<HierarchyTable> comparer) {
List<HierarchyTable> sorted = new List<HierarchyTable>(){
this
};
if (Children.Count > 0) {
//------------------------------------------------
// Create sorted copy of children using IComparer
//------------------------------------------------
List<HierarchyTable> children = Children.ToList();
children.Sort(comparer);
foreach (HierarchyTable child in children)
sorted.AddRange(child.FlatSort(comparer));
}
return sorted;
}
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 {
return 0;
}
}
}
样本使用:
List<HierarchyTable> flatsort = hierarchyTableInstance.FlatSort(new HierarchyTableComparer());
foreach (HierarchyTable item in flatsort)
Console.WriteLine(item);