Как извлечь каждый элемент из базы данных, вам нужно добавить его к родителю. Поэтому держите словарь, чтобы помочь найти родителя. Если вы получаете ребенка до его родителя, тогда вы можете вставить местозаполнитель, пока не получите реальную вещь.
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(), чтобы действовать как заполнитель, в случае, если ребенок извлекается перед его родителем. Вы можете избежать этого требования, если знаете что-то о порядке объектов, или если вы обрабатываете словарь в два прохода, как показано в других ответах.
Я использую значение часового, _parentGroupName, чтобы поддерживать узлы верхнего уровня в той же коллекции, что и все остальные. Вы можете легко изменить это, чтобы вместо этого использовать отдельную коллекцию для узлов верхнего уровня.
Спасибо за ответы на все вопросы. Я собираюсь проверить, у меня есть рабочее решение, прежде чем отвечать на ответ. :) – Echilon