[英]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.