I am trying to get all menus and children that satisfy those conditions using linq:
- Menus should have either a link or children count > 0 to be displayed at the
website - For the menus that has children : they should have at least one child menu that has a link
This is the Menu class:
public class Menu
{
public string Name { get; set; }
public string Link { get; set; }
public List<Menu> Children { get; set; }
public Menu()
{
Children = new List<Menu>();
}
}
Suppose we have this data structure:
List<Menu> root = new List<Menu>();
Menu parent_1 = new Menu() { Name = "Parent 1", Link = null };
Menu parent_2 = new Menu() { Name = "Parent 2", Link = null };
//children for parent 1
Menu p1_child_1 = new Menu() { Name = "p1_child_1", Link = null };
Menu p1_child_2 = new Menu() { Name = "p1_child_2", Link = null };
//sub children of p1_child_2
Menu p1_child_1_1 = new Menu() { Name = "p1_child_1_1", Link = "l1-1" };
Menu p1_child_1_2 = new Menu() { Name = "p1_child_1_2", Link = null };
p1_child_1.Children.AddRange(new List<Menu> { p1_child_1_1 , p1_child_1_2 });
parent_1.Children.AddRange(new List<Menu> { p1_child_1, p1_child_2 });
Menu p2_child_1 = new Menu() { Name = "p2_child_1", Link = null };
Menu p2_child_2 = new Menu() { Name = "p2_child_2", Link = "l2-2" };
Menu p2_child_1_1 = new Menu() { Name = "p2_child_1_1", Link = null };
Menu p2_child_1_2 = new Menu() { Name = "p2_child_1_2", Link = null };
p2_child_1.Children.AddRange(new List<Menu> { p2_child_1_1, p2_child_1_2 });
parent_2.Children.AddRange(new List<Menu> { p2_child_1, p2_child_2 });
root.Add(parent_1);
root.Add(parent_2);
Result: The filtered list returned based on the conditions requested will be:
parent_1
p1_child_1
- p1_child_1_1
parent_2
- p2_child_2
How to achieve that using Linq or alternative approach taking into consideration the menu could have up to many levels?
Trying the solution as proposed in the comments, i added the extension method
public static IEnumerable<TResult> SelectHierarchy<TResult>(this IEnumerable<TResult> source, Func<TResult, IEnumerable<TResult>> collectionSelector, Func<TResult, bool> predicate)
{
if (source == null)
{
yield break;
}
foreach (var item in source)
{
if (predicate(item))
{
yield return item;
}
var childResults = SelectHierarchy(collectionSelector(item), collectionSelector, predicate);
foreach (var childItem in childResults)
{
yield return childItem;
}
}
Then called the method:
var result = root.SelectHierarchy(n => n.Children, n => n.Children.Count > 0 || n.Link != null).ToList();
However this is not what i want, I expect two menus which carry the subMenus that satisfy my condition, but i am getting 6 menus which i guess are flattened.
Although, p2_child_1
was returned since children count > 0, however it shouldn't cause its menus has no links. ( I placed the predicate as above, since i don't have other option.