65.9K
CodeProject is changing. Read more.
Home

Select Recursive Extension method

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (4 votes)

May 12, 2010

CPOL
viewsIcon

22853

An extension method to the IEnumerable`1 interface that can select all children recursively of an entity in a tree like structure

This is an extension method to the IEnumerable`1 interface that can select all children recursively of an entity in a tree like structure.

Example

var allChildren = node.ChildNodes.SelectRecursive(n => n.ChildNodes);
    /// <summary>
    /// A set of <see cref="T:System.Collections.Generic.IEnumerable`1" /> extensions methods
    /// </summary>
    public static class IEnumerableExtensions
    {
        /// <summary>
        /// Projects each element of a sequence recursively to an <see cref="T:System.Collections.Generic.IEnumerable`1" /> 
        /// and flattens the resulting sequences into one sequence. 
        /// </summary>
        /// <typeparam name="T">The type of the elements.</typeparam>
        /// <param name="source">A sequence of values to project.</param>
        /// <param name="selector">A transform function to apply to each element.</param>
        /// <returns>
        /// An <see cref="T:System.Collections.Generic.IEnumerable`1" /> whose elements 
        /// who are the result of invoking the recursive transform function on each element of the input sequence. 
        /// </returns>
        /// <example>
        /// node.ChildNodes.SelectRecursive(n => n.ChildNodes);
        /// </example>
        public static IEnumerable<IRecursion<T>> SelectRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
        {
            return SelectRecursive(source, selector, null);
        }
        /// <summary>
        /// Projects each element of a sequence recursively to an <see cref="T:System.Collections.Generic.IEnumerable`1" /> 
        /// and flattens the resulting sequences into one sequence. 
        /// </summary>
        /// <typeparam name="T">The type of the elements.</typeparam>
        /// <param name="source">A sequence of values to project.</param>
        /// <param name="selector">A transform function to apply to each element.</param>
        /// <param name="predicate">A function to test each element for a condition in each recursion.</param>
        /// <returns>
        /// An <see cref="T:System.Collections.Generic.IEnumerable`1" /> whose elements are the result of 
        /// invoking the recursive transform function on each element of the input sequence. 
        /// </returns>
        /// <example>
        /// node.ChildNodes.SelectRecursive(n => n.ChildNodes, m => m.Depth < 2);
        /// </example>
        public static IEnumerable<IRecursion<T>> SelectRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector, Func<IRecursion<T>, bool> predicate)
        {
            return SelectRecursive(source, selector, predicate, 0);
        }
        private static IEnumerable<IRecursion<T>> SelectRecursive<T>(IEnumerable<T> source, Func<T, IEnumerable<T>> selector, Func<IRecursion<T>, bool> predicate, int depth)
        {
            var q = source
                .Select(item => new Recursion<T>(depth, item))
                .Cast<IRecursion<T>>();
            if (predicate != null)
                q = q.Where(predicate);
            foreach (var item in q)
            {
                yield return item;
                foreach (var item2 in SelectRecursive(selector(item.Item), selector, predicate, depth + 1))
                    yield return item2;
            }
        }
        private class Recursion<T> : IRecursion<T>
        {
            private int _depth;
            private T _item;
            public int Depth
            {
                get { return _depth; }
            }
            public T Item
            {
                get { return _item; }
            }
            public Recursion(int depth, T item)
            {
                _depth = depth;
                _item = item;
            }
        } 
    }

    /// <summary>
    /// Represents an item in a recursive projection.
    /// </summary>
    /// <typeparam name="T">The type of the item</typeparam>
    public interface IRecursion<T>
    {
        /// <summary>
        /// Gets the recursive depth.
        /// </summary>
        /// <value>The depth.</value>
        int Depth { get; }
        /// <summary>
        /// Gets the item.
        /// </summary>
        /// <value>The item.</value>
        T Item { get; }
    }