Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A C# Extension to Recursively Select All Descendents

0.00/5 (No votes)
9 Mar 2017 1  
How to recursively select all descendants using an extension method

Introduction

.NET's SelectMany extension methods select only the immediate children of the source. Here, I present two IEnumerable<T> extension methods called SelectManyRecursive and SelectManyAllInclusive. The first will select only the descendents and the second will select the descendents and the items in the source collection. I include also a test program to show how it works.

Using the Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace SelectManyRecursive
{
    public static class Extensions
    {
        public static IEnumerable<T> SelectManyRecursive<T>
                (this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
        {
            var result = source.SelectMany(selector);
            if (!result.Any())
            {
                return result;
            }
            return result.Concat(result.SelectManyRecursive(selector));
        }
 
        public static IEnumerable<T> SelectManyAllInclusive<T>
               (this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
        {
            return source.Concat(source.SelectManyRecursive(selector));
        }
    }
 
    class Test
    {
        int _level;
        public List<Test> Children = new List<Test>();
 
        static Random rand = new Random(DateTime.Now.Millisecond);
        public static int maxLevels = 5;
        public static int total = 0;
 
        public Test() : this(1)
        {
        }
 
        private Test(int level)
        {
            ++total;
            _level = level;
            if (level < maxLevels)
            {
                int numChildren = rand.Next(10) + 1;
                ++level;
                while (numChildren-- > 0)
                {
                    Children.Add(new Test(level));
                }
            }
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            List<Test> root = new List<Test> { new Test(), new Test() };
            var descendents = root.SelectManyRecursive(t => t.Children);
            int descendantsCount = descendents.Count();
 
            var all = root.SelectManyAllInclusive(t => t.Children);
            var count = all.Count();
 
            Console.WriteLine($@"Total Test instances = {Test.total}
Total descendents collected = {descendantsCount}.
 
Note that 'Total descendents' will correctly be {root.Count} less than
'Total Test instances' because SelectManyRecursive selects only the descendents.
 
SelectManyAllInclusive (count = {count}) will select all items including from the source.
 
Press enter to continue:");
            Console.Read();
        }
    }
}

Points of Interest

There's a more efficient non-recursive algorithm than the one presented.

History

  • 8 March 2017: First version
  • 9 March 2017: changed to use result.Any() instead of .ToList() and .Count because .ToList() can result in unnecessary overhead.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here