У меня есть иерархическое меню. Пункты меню выглядит следующим образом:Обработка иерархической коллекции с помощью Linq
public struct MenuElement
{
public int Id {get; set;}
public int Position {get; set;}
public string Label {get; set;}
public int HierarchicalLevel {get; set;}
public int ParentLevelId {get; set;}
public bool IsMandatory {get; set;}
}
Структура моего меню управляется с классом узла:
public class Node<T>
{
public T Item {get; set;}
public IEnumerable<Node<T>> ChildrenNodes {get; set;}
public int HierarchicalLevel {get; set;}
}
пункты меню извлекаются из базы данных. Так что, когда я строю свое меню, и я просматриваю мое меню:
// Get the menu items from database
List<MenuElement> flattenMenuElements = GetMenuItemsFromDb();
// Build the hierarchical menu with relationships between nodes (parentId, ChildrenNodes...)
List<Node<MenuElement>> hierarchicalMenu = BuildMenu(flattenMenuElements);
foreach(var node in Browse(hierarchicalMenu))
{
string space = ""
for(int i=0; i<node.HierarchicalLevel; i++)
space = String.Concat(space, " ");
Console.Writeline("{0}{1}. {2}",space, node.Item.Position, node.Item.Label);
}
//Browse method
IEnumerable<Node<MenuElement>> Browse(IEnumerable<Node<MenuElement>> nodes)
{
foreach(var node in nodes)
{
yield return node;
foreach (var childNode in Browse(node.ChildrenNodes))
{
yield return childNode;
}
}
}
Консоль вывода:
1. LabelMenu1
1. LabelMenu11
1. LabelMenu111
2. LabelMenu112
3. LabelMenu113
2. LabelMenu12
3. LabelMenu13
2. LabelMenu2
3. LabelMenu3
1. LabelMenu31
...
Так что результат является то, что я ожидал. Но теперь я хочу получить только MenuElement со свойством IsMandatory == false AND THEIR PARENTS (и их grand-parents и т. Д.). Например, в приведенном выше меню только LabelMenu112 и LabelMenu31 имеют свойство IsMandatory, установленное на false. Поэтому я хочу, если я просматриваю мое меню результат вывода будет выглядеть следующим образом:
1. LabelMenu1
1. LabelMenu11
2. LabelMenu112
3. LabelMenu3
1. LabelMenu31
На самом деле, чтобы сделать это, я перестраивать второе меню из первого иерархического меню после фильтрации по элементам меню я хочу сохранить:
// Get the menu elements from database
List<MenuElement> flattenMenuElements = GetMenuItemsFromDb();
// Build the hierarchical menu with relationships between nodes (parentId, ChildrenNodes...)
List<Node<MenuElement>> hierarchicalMenu = BuildMenu(flattenMenuElements);
// Get a flat list of menu elements where the property IsMandatory is set to false
List<MenuElement> filteredMenuElements = flattenMenuElements.Where(m => m.IsMandatory == false).ToList();
// Get a flat list of filtered menu elements AND THEIR PARENTS, GRAND PARENTS etc
List<MenuElement> filteredMenuElementsWithParents = GetMenuElementsWithParents(hierarchicalMenu, filteredMenuElements).ToList();
List<MenuElement> GetMenuElementsWithParents(IEnumerable<Node<MenuElement>> hierarchicalMenu, IEnumerable<MenuElement> filteredMenuElements)
{
List<MenuElement> menu = new List<MenuElement>();
foreach (var item in filteredMenuElements)
{
menu.Add(item);
AddParentNode(item, menu, hierarchicalMenu);
}
}
void AddParentNode(MenuElement element, List<MenuElement> menu, IEnumerable<Node<MenuElement>> hierarchicalMenu)
{
if (element.ParentLevelId != default(int))
{
// Get the parent node of element
MenuElement menuEl = Browse(hierarchicalMenu)
.Where(node => node.Item.Id == element.ParentLevelId)
.Select(node => node.Item)
.First();
if(!menu.Contains(menuEl))
menu.Add(menuEl);
AddParentNode(menuEl, menu, hierarchicalMenu);
}
}
Это решение работает, но мне интересно, если это примечание можно использовать возможности LINQ, чтобы получить эти отфильтрованные элементы меню и их родителей, а не строить плоский список, а затем построить второе иерархическое меню из плоского списка.
Есть ли способ сделать это с помощью Linq?
Спасибо!
С уважением,
Florian
Я еще не прочитал весь ваш вопрос, но если вы собираетесь создать изменчивую структуру для 'MenuElement', у вас будет плохое время. EDIT: Не могли бы вы его обновить, чтобы при вызове 'BuildMenu' каждому« узлу »была назначена прямая ссылка на родительский узел? Тогда становится тривиальнее ползать по дереву. –
Да, в BuildMenu каждому узлу присваивается его родительский элемент с свойством ParentId, а также заполняются childrenNodes. – Florian
@Florian вы могли бы улучшить условия фильтрации? Вы сказали: «... со свойством IsMandatory == false AND THEIR PARENTS ...», я могу понять первое предложение, но как насчет «... И ИХ РОДИТЕЛЕЙ ...»? –