构建在C#中将树排序为列表

时间:2011-10-17 00:37:58

标签: c#

我有一个C#实体列表。我的实体定义如下:

public class Item
{
    // the id of an item
    public Guid ID { get; set; }

    // if this is a child item, the ParentID is the ID of the item that
    // this item is a child of
    public Guid? ParentID { get; set; }

    // If this item does not have a parent, this should be 0.
    // Otherwise if it is a child, a level=1
    // If it is a grandchild, level=2, etc.
    public int Level { get; set; }

    // The UTC date the item was created on.
    public DateTime CreateDate { get; set; }
}

我的这些实体列表是随机顺序。我试图弄清楚如何对实体列表进行排序,使得项目元素按级别(升序)排序,然后按createDate(升序)排序。基本上,列表看起来像这样:

Item 1 (Level 0)
  Item 2 (Level 1)
    Item 3 (Level 2)
    Item 4 (Level 2)
  Item 5 (Level 1)
Item 6 (Level 0)
  Item 7 (Level 2)
etc.

这似乎很容易。也许我一直在看它太久了。但我似乎可以让它发挥作用。有任何想法吗?

3 个答案:

答案 0 :(得分:4)

即使你说你希望按级别(升序)和createDate(升序)排序的项目,你的图表也是如此。看起来你真的希望以允许你打印出一棵树的方式对项目进行排序,这样每个项目都在其父项之后,在其子项及其父项“年轻的兄弟姐妹”之前。这是一个topological sort,它与你要求的大不相同,但并不那么难:

class ItemComparer : IComparer<Item>
{
    // allow us to look up parent Items by GUID
    IDictionary<Guid, Item> itemLookup;

    public ItemComparer(IEnumerable<Item> list)
    {
        itemLookup = list.ToDictionary(item => item.ID);
        foreach (var item in list)
            SetLevel(item);
    }

    int SetLevel(Item item)
    {
        if (item.Level == 0 && item.ParentID.HasValue)
            item.Level = 1 + itemLookup[item.ParentID.Value].Level;
        return item.Level;
    }

    public int Compare(Item x, Item y)
    {
        // see if x is a child of y
        while (x.Level > y.Level)
        {
            if (x.ParentID == y.ID)
                return 1;
            x = itemLookup[x.ParentID.Value];
        }
        // see if y is a child of x
        while (y.Level > x.Level)
        {
            if (y.ParentID == x.ID)
                return -1;
            y = itemLookup[y.ParentID.Value];
        }
        // x and y are not parent-child, so find common ancestor
        while (x.ParentID != y.ParentID)
        {
            x = itemLookup[x.ParentID.Value];
            y = itemLookup[y.ParentID.Value];
        }
        // compare createDate of children of common ancestor
        return x.CreateDate.CompareTo(y.CreateDate);
    }
}

像这样调用它:

// if List<Item>
items.Sort(new ItemComparer(items));
// if IEnumerable<Item>
var sorted = random.OrderBy(x => x, new ItemComparer(random));

答案 1 :(得分:4)

这是一个替代答案,它从输入构建树,然后按顺序遍历它。此遍历为您提供排序输出。

class FlattenTree
{
    // map each item to its children
    ILookup<Item, Item> mapping;

    public FlattenTree(IEnumerable<Item> list)
    {
        var itemLookup = list.ToDictionary(item => item.ID);
        mapping = list.Where(i => i.ParentID.HasValue)
                      .ToLookup(i => itemLookup[i.ParentID.Value]);
    }

    IEnumerable<Item> YieldItemAndChildren(Item node)
    {
        yield return node;
        foreach (var child in mapping[node].OrderBy(i => i.CreateDate))
            foreach (var grandchild in YieldItemAndChildren(child))
                yield return grandchild;
    }

    public IEnumerable<Item> Sort()
    {
        return from grouping in mapping
               let item = grouping.Key
               where item.ParentID == null
               orderby item.CreateDate
               from child in YieldItemAndChildren(item)
               select child;
    }
}

像这样调用它:

var sorted = new FlattenTree(random).Sort();

答案 2 :(得分:0)

从字面上看,要对列表进行排序,您应该使用名为Sort的方法。您只需要提供正确的比较器,这意味着您需要定义一个方法来比较您定义的两个项目(按级别,然后创建)。在这里查看MSDN: http://msdn.microsoft.com/en-us/library/w56d4y5z.aspx

例如,您可以使用这样的lambda:

mylist.Sort((x,y) => {
  int c = x.Level.CompareTo(y.Level);
  if(c==0) c = x.CreateDate.CompareTo(y.CreateDate);
  return c;
});

请注意,我没有在Visual Studio中编译和测试此代码。也许这不是100%正确,但希望它能告诉你从哪里开始。