Patrick Peters
Patrick Peters

Reputation: 9578

Merge nested (same type) with LINQ to objects

Considering the following class:

public class MenuItemModel
    {
        public MenuItemModel()
        {
            this.items = new List<MenuItemModel>();
        }

        [DataMember]
        public int id { get; set; }

        [DataMember]
        public string name { get; set; }

        [DataMember]
        public string type { get; set; }

        [DataMember]
        public string route { get; set; }

        [DataMember]
        public ProcessIndex ProcesIndex { get; set; }

        [DataMember]
        public bool isCapable { get; set; }

        [DataMember]
        public List<MenuItemModel> items { get; set; }
    }

I have a list of these MenuItemModel instances. Some of those instance have subitems (in the items property).

I want to grab all the menuitems, including the subitems with a LINQ to objects query that matches a given processindex, how can this be done ?

Upvotes: 0

Views: 68

Answers (1)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236248

You can use following extension method to flatten menu items hierarchy

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> childrenSelector)
{
    foreach(var item in source)
    {
        yield return item;
        foreach(var child in childrenSelector(item).Flatten(childrenSelector))
           yield return child;
    }
}

Or non-recursive one:

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> childrenSelector)
{
    Stack<T> items = new Stack<T>(source);

    while (items.Any())
    {
        T item = items.Pop();
        yield return item;

        foreach (var child in childrenSelector(item))
            items.Push(child);
    }
}

Usage:

var allItems = items.Flatten(i => i.items);

You can use simple Enumerable.Where filter to get only items with required process index.

Upvotes: 1

Related Questions