我有一个基本上形成树的嵌套注释列表。
//Non-Relevant properties (Like Body, Creator etc) excluded
internal class Comment
{
public int ID { get; set; }
public int ItemID { get; set; }
public int ParentItemID { get; set; }
public List<Comment> ChildComments { get; set; }
}
首先,我从数据库中获取Posts
的列表,然后我得到所有Comments where ItemID == Posts[all].id
我想遍历一个平面List并创建一个嵌套或树列表。
它是这样的:
如果是ParentItemID == 0
,那么我们会有一个顶级评论。如果ParentItemID > 0
我们需要将ParentItemID
与评论ID
匹配,并将子评论添加到父评论列表。
我被困的地方是,我过去只使用递归来浏览文件和文件夹。这不允许我实例化一个集合并通过后续递归来保持它。
在递归之外实例化列表似乎很愚蠢,然后每次我想添加项目时遍历所有项目。
我确信这样做有一个很好的固体模式,我找不到它。
答案 0 :(得分:4)
您可以循环浏览所有评论并获取每个评论的孩子评论:
foreach (Comment comment in comments) {
comment.ChildComments =
comments.Where(c => c.ParentItemID == comment.ItemID).ToList();
}
另一种表现更好的选择(如果需要的话)是将评论分组到ParentItemID
并从中创建Dictionary<int, List<Comment>>
,然后循环浏览上面的评论并获取从字典中列出。
答案 1 :(得分:3)
Guffa的解决方案更漂亮,但这里有另一种选择:
public static List<Comment> ToTree(List<Comment> FlatCommentsList)
{
List<Comment> TopComments = FlatCommentsList.Where<Comment>(x => x.ParentItemID == 0).ToList();
List<Comment> NestedComments = FlatCommentsList.Where<Comment>(x => x.ParentItemID > 0).ToList();
List<int> IdsToRemove;
while (NestedComments.Count > 0)
{
IdsToRemove = new List<int>();
NestedComments.ForEach(x =>
{
Comment ParentComment = TopComments.Where<Comment>(y => y.ItemID == x.ParentItemID).SingleOrDefault<Comment>();
if (ParentComment != null)
{
ParentComment.ChildComments.Add(x);
IdsToRemove.Add(x.ItemID);
}
});
NestedComments.RemoveAll(x => IdsToRemove.Contains(x.ItemID));
}
return TopComments;
}
它也没有递归,但它完成了它的工作。我非常喜欢递归,但我认为我们不需要它。
答案 2 :(得分:1)
可以使用LINQ和GroupBy
:
var grouped = comments.GroupBy(c => c.ParentItemId == 0);
var topLevel = grouped.Single(g => g.Key);
var nested = grouped.Single(g => !g.Key);
(在迭代序列或对它们使用诸如ToList()
之类的方法之前,实际上不会计算这些操作的结果)
将嵌套评论附加到他们的父母可以像Guffa的方法一样(很多评论很慢,不使用额外的内存)或者你可以做一个经典的时空权衡(很快就有很多评论,需要)额外的记忆):
var dictionary = comments.ToDictionary(c => c.Id);
foreach (nested as comment) {
dictionary[comment.ParentItemId].ChildComments.Add(comment);
}