繁体   English   中英

C#-递归分组

[英]C# - Recursive Grouping

我有一个IGroup类型的对象列表。 它们可以嵌套到一个最高级别,从数据库中检索它们之后,我试图对其进行分组。 我无法解决如何将所有组递归添加到正确的父母的问题。 父级为null的任何组都是顶级组。 我不能保证它们从数据库中出来的顺序。

public interface IGroup {
  string ID { get; set; }
  string Name { get; set; }
  string ParentID { get; set; }
  IList<IGroup> Groups { get; set; }
  ...

因此,如果我有以下列表:

Group1: ID = g1, ParentID = null
Group1a: ID = g2, ParentID = g1
Group2: ID = g3, ParentID = null
Group1b: ID = g4, ParentID = g3
Group1bc: ID = g5, ParentID = g4

我正在尝试将它们分组为:

|Group1
|--Group1a
|--Group1b
|--|
   |--Group1bc
|Group2

任何人都想递归地将它们分组吗?

无需递归。 以机智:

var lookup = items.ToDictionary(g => g.ID); // items is IEnumerable<IGroup>
foreach (var item in items.Where(g => g.ParentID != null)) {
    lookup[item.ParentID].Groups.Add(item);
}
var parents = items.Where(g => g.ParentID == null);

请注意,如果不存在带有相应ParentID IGroup ,则会抛出lookup[item.ParentID] 您可以使用TryGetValue更优雅地处理此问题。

我的IGroup实现:

public class Group : IGroup {
    public string ID { get; set; }
    public string Name { get; set; }
    public string ParentID { get; set; }
    public IList<IGroup> Groups { get; set; }
    public Group() {
        Groups = new List<IGroup>();
    }
}

我的测试项目:

IEnumerable<IGroup> items = new List<IGroup>() {
    new Group() { ID = "g1", ParentID = null },
    new Group() { ID = "g2", ParentID = "g1" },
    new Group() { ID = "g3", ParentID = null },
    new Group() { ID = "g4", ParentID = "g3" },
    new Group() { ID = "g5", ParentID = "g4" },
    new Group() { ID = "g6", ParentID = "g5" }
};    

这不是递归的,但是这是一个解决方案(假设您在名为groups的列表中拥有所有groups

var rootGroups = new List<IGroup>();
var dic = groups.ToDictionary(g => g.ID);
foreach (var g in groups)
{
    if (g.ParentID == null)
    {
        rootGroups.Add(g);
    }
    else
    {
        IGroup parent;
        if (dic.TryGetValue(g.ParentID, out parent))
        {
                parent.Groups.Add(g);
        }
    }
}

您可以尝试按父ID对其进行排序,假设父组始终在子组之前创建。

ParentID (Linq: GroupBy ),按ID排序。

从一个空的根节点(ID: null )开始,并添加所有具有此ParentID的项。 对于已添加的任何项目,递归地继续此过程。

从数据库中提取每个元素时,需要将其添加到其父级。 因此,保留词典以帮助找到父母。 如果您在孩子的父母之前生了一个孩子,则可以插入一个占位符,直到获得真实的东西为止。

void BuildGroups()
{
   foreach( IGroup x /* obtained from database or a collection or wherever */ )
      AddGroup( x );
}

Dictionary<string,IGroup> _groups = new Dictionary<string,IGroup>;
string _parentGroupName = "PARENT";
void AddGroup( IGroup x )
{
    // locate (or create) parent and add incoming group
    IGroup parent;
    string parentID = x.ParentID ?? _parentGroupName;
    if( !groups.TryGetValue( parentID, out parent ) )
    {
       parent = new Group( parentID ); // SEE NOTE BELOW!
       _groups[parentID] = parent;
    }
    parent.Groups.Add( x );

    // locate (or insert) child, and make sure it's up to date
    IGroup child;
    if( groups.TryGetValue( x.ID, out child ) )
    {
       // We must have inserted this ID before, because we found one
       // of ITS children first.  If there are any other values besides
       // ParentID and ID, then copy them from X to child here.
    }
    else
    {
       // first time we've seen this child ID -- insert it
        _groups[x.ID] = x;
    }

}

_parentGroupName处的dictionary元素将是一个虚拟节点,其子节点是您所有的顶级组(即,数据库中ParentID为NULL的那些子组); 从该元素,您可以进行递归遍历:

VisitGroups( _groups[_parentGroupName], "" );

void VisitGroups( string ID, string indent )
{
   IGroup x;
   if( _groups.TryGetValue( ID, out x ) )
   {
       foreach( IGroup y in x.Groups )
       {
          WriteLine( indent + " {0}", y.ID );
          VisitGroups( y.ID, indent + "   " );
       }        
   }
}

注意:此实现在数据中的单次内联传递中运行-您可以在从数据库中检索元素时立即添加元素,而您只需要对数据进行一次传递。 这意味着您可以节省一些时间和内存。 但是作为回报,它要求您能够分配一个IGroup()类型的对象以充当占位符,以防在其父级之前检索到一个子级。 如您对对象的顺序有所了解,或者如果您两次处理字典就可以避免这种要求,如其他答案所示。

我使用哨兵值_parentGroupName将顶级节点与所有其他节点保持在同一集合中。 如果愿意,您可以轻松地对此进行更改,以对顶层节点使用单独的集合。

你可以试试这个

public interface IGroup
{
    string ID { get; set; }  
    string Name { get; set; }  
    string ParentID { get; set; }  
    List<IGroup> Groups { get; set; }
}
public class Group : IGroup
{
    public string ID { get; set; }
    public string Name { get; set; }
    public string ParentID { get; set; }
    public List<IGroup> Groups { get; set; }
    public Group()
    {

    }
    public Group(string id, string name, List<IGroup> childs)
    {
        ID = id;
        Name = name;
        Groups = (List<IGroup>)childs.Cast<IGroup>();
    }

}


class Program
{
    static void Main(string[] args)
    {
        List<IGroup> OriginalList;
        List<IGroup> HirarchList = new List<IGroup>();

        OriginalList = new List<IGroup>() 
        { 
            new Group() { ID = "g1", ParentID = null }, 
            new Group() { ID = "g2", ParentID = "g1" }, 
            new Group() { ID = "g3", ParentID = null }, 
            new Group() { ID = "g4", ParentID = "g3" }, 
            new Group() { ID = "g5", ParentID = "g4" }, 
            new Group() { ID = "g6", ParentID = "g5" } };

        HirarchList = GetCreateList(null,  OriginalList);
    }
    public static List<IGroup> GetCreateList(string id, List<IGroup> list)
    {
        List<IGroup> temp = new List<IGroup>();
        temp = (from item in list 
               where item.ParentID == id
               select (IGroup)new Group(item.ID, item.Name,GetCreateList(item.ID, list))).ToList();

        return (List<IGroup>)temp;
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM