从列表中递归创建树

时间:2012-08-28 22:51:07

标签: c# generics recursion tree

我有一个基本上形成树的嵌套注释列表。

//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匹配,并将子评论添加到父评论列表。

我被困的地方是,我过去只使用递归来浏览文件和文件夹。这不允许我实例化一个集合并通过后续递归来保持它。

在递归之外实例化列表似乎很愚蠢,然后每次我想添加项目时遍历所有项目。

我确信这样做有一个很好的固体模式,我找不到它。

3 个答案:

答案 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);
}