我有一个IGroup类型的对象列表。这些可以嵌套到umlimited级别,我试图在从数据库中检索它们之后对它们进行分组。我无法理解如何递归地将所有组添加到正确的父母。任何以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
有人想要递归地将它们分组吗?
答案 0 :(得分:6)
无需递归。即:
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);
请注意,如果lookup[item.ParentID]
没有相应的IGroup
,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" }
};
答案 1 :(得分:0)
您可以尝试按父ID排序,假设父组始终在子组之前创建。
答案 2 :(得分:0)
按ParentID
分组(Linq:GroupBy
),按ID
排序。
从空的根节点(ID:null
)开始,并添加具有此ParentID的所有项目。对已添加的任何项目递归地继续此过程。
答案 3 :(得分:0)
从数据库中提取每个元素时,需要将其添加到其父元素中。所以保持一个字典来帮助找到父母。如果你的孩子在其父母之前得到了孩子,那么你可以插入一个占位符,直到你得到真实的东西。
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的字典元素将是一个虚拟节点,其子节点都是您的顶级组(即数据库中具有NULL作为ParentID的那些);从该元素中,您可以进行递归遍历:
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()类型的对象作为占位符,以防在其父级之前检索子级。如果您对对象的顺序有所了解,或者如果您在两遍中处理字典,则只能避免该要求,如其他答案所示。
我使用sentinel值_parentGroupName将顶级节点保存在与其他所有节点相同的集合中。如果您愿意,可以轻松地对此进行更改,以便为顶级节点使用单独的集合。
答案 4 :(得分:0)
这不是递归的,但这是一个解决方案(假设您在名为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);
}
}
}
答案 5 :(得分:0)
你可以试试这个
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;
}
}