2

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.

enter image description here

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.

Hussein Salman
  • 7,806
  • 15
  • 60
  • 98
  • 5
    Linq is not designed for recursive queries. You'd have to create some iterator that iterates recursively then query _that_. If you have something that works Linq isn't going to help a whole lot. – D Stanley Jan 09 '17 at 20:23
  • So How to do that in an efficient way regardless of linq – Hussein Salman Jan 09 '17 at 20:26
  • Can you please provide both the sample data and the expected output as valid C# code? So, for example, if you wanted to add two numbers together you might provide us with: `int x = 3; int y = 5;` and I want the result `int z = 8`. We can then be sure that when we provide the code `x + y` we know we're going to give you the result you want. – Enigmativity Jan 09 '17 at 20:27
  • please have a look at these posts http://stackoverflow.com/questions/5422735/how-do-i-select-recursive-nested-entities-using-linq-to-entity http://stackoverflow.com/questions/19237868/get-all-children-to-one-list-recursive-c-sharp – Chandan Rai Jan 09 '17 at 20:30
  • You can check my answer on this post http://stackoverflow.com/questions/40656366/iterate-through-objects-with-lists-of-the-object/40657294#40657294 – Franck Jan 09 '17 at 20:45
  • @Enigmativity How the question marked as duplicate (which is for **searching** tree) does solve building a **filtered** tree? – Ivan Stoev Jan 09 '17 at 20:58
  • @IvanStoev - The OP asks for "The filtered list" so it seems to me that this is a valid duplicate. D Stanley obviously feels the same. Otherwise it's really very difficult to properly interpret questions that don't provide clear inputs and outputs in valid C# code. A better question might have clarified that better. – Enigmativity Jan 09 '17 at 21:01
  • 1
    @Enigmativity Looking at the sample data and the desired result, it's obvious (to me) that OP has a list with root nodes and wants to get a new list with modified nodes with some nodes eliminated (like `child 1` and `child 1.1`). Also he's asking for *LINQ **or alternative approach***. But anyway, I guess you know better :) – Ivan Stoev Jan 09 '17 at 21:09
  • 1
    @IvanStoev - I'm happy to be corrected, but the OP needs to weigh in with his actual requirements. If he pings me I'll reopen if need be. – Enigmativity Jan 09 '17 at 21:26
  • @Enigmativity can you please reopen it – Hussein Salman Jan 09 '17 at 21:35
  • @h.salman - Well done for such a good update to your question. – Enigmativity Jan 09 '17 at 23:43
  • for a html `
  • ` menu, [`XElement`](https://msdn.microsoft.com/en-us/library/mt693113.aspx) should be easier to filter and generater the html
  • – Slai Jan 10 '17 at 00:40