Recursive List Flattening

后端 未结 13 1279
既然无缘
既然无缘 2020-11-27 04:25

I could probably write this myself, but the specific way I\'m trying to accomplish it is throwing me off. I\'m trying to write a generic extension method similar to the oth

相关标签:
13条回答
  • 2020-11-27 05:08

    I had to implement mine from scratch because all of the provided solutions would break in case there is a loop i.e. a child that points to its ancestor. If you have the same requirements as mine please take a look at this (also let me know if my solution would break in any special circumstances):

    How to use:

    var flattenlist = rootItem.Flatten(obj => obj.ChildItems, obj => obj.Id)
    

    Code:

    public static class Extensions
        {
            /// <summary>
            /// This would flatten out a recursive data structure ignoring the loops. The end result would be an enumerable which enumerates all the
            /// items in the data structure regardless of the level of nesting.
            /// </summary>
            /// <typeparam name="T">Type of the recursive data structure</typeparam>
            /// <param name="source">Source element</param>
            /// <param name="childrenSelector">a function that returns the children of a given data element of type T</param>
            /// <param name="keySelector">a function that returns a key value for each element</param>
            /// <returns>a faltten list of all the items within recursive data structure of T</returns>
            public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source,
                Func<T, IEnumerable<T>> childrenSelector,
                Func<T, object> keySelector) where T : class
            {
                if (source == null)
                    throw new ArgumentNullException("source");
                if (childrenSelector == null)
                    throw new ArgumentNullException("childrenSelector");
                if (keySelector == null)
                    throw new ArgumentNullException("keySelector");
                var stack = new Stack<T>( source);
                var dictionary = new Dictionary<object, T>();
                while (stack.Any())
                {
                    var currentItem = stack.Pop();
                    var currentkey = keySelector(currentItem);
                    if (dictionary.ContainsKey(currentkey) == false)
                    {
                        dictionary.Add(currentkey, currentItem);
                        var children = childrenSelector(currentItem);
                        if (children != null)
                        {
                            foreach (var child in children)
                            {
                                stack.Push(child);
                            }
                        }
                    }
                    yield return currentItem;
                }
            }
    
            /// <summary>
            /// This would flatten out a recursive data structure ignoring the loops. The     end result would be an enumerable which enumerates all the
            /// items in the data structure regardless of the level of nesting.
            /// </summary>
            /// <typeparam name="T">Type of the recursive data structure</typeparam>
            /// <param name="source">Source element</param>
            /// <param name="childrenSelector">a function that returns the children of a     given data element of type T</param>
            /// <param name="keySelector">a function that returns a key value for each   element</param>
            /// <returns>a faltten list of all the items within recursive data structure of T</returns>
            public static IEnumerable<T> Flatten<T>(this T source, 
                Func<T, IEnumerable<T>> childrenSelector,
                Func<T, object> keySelector) where T: class
            {
                return Flatten(new [] {source}, childrenSelector, keySelector);
            }
        }
    
    0 讨论(0)
  • 2020-11-27 05:10

    Hmm... I'm not sure exactly what you want here, but here's a "one level" option:

    public static IEnumerable<TElement> Flatten<TElement,TSequence> (this IEnumerable<TSequence> sequences)
        where TSequence : IEnumerable<TElement> 
    {
        foreach (TSequence sequence in sequences)
        {
            foreach(TElement element in sequence)
            {
                yield return element;
            }
        }
    }
    

    If that's not what you want, could you provide the signature of what you do want? If you don't need a generic form, and you just want to do the kind of thing that LINQ to XML constructors do, that's reasonably simple - although the recursive use of iterator blocks is relatively inefficient. Something like:

    static IEnumerable Flatten(params object[] objects)
    {
        // Can't easily get varargs behaviour with IEnumerable
        return Flatten((IEnumerable) objects);
    }
    
    static IEnumerable Flatten(IEnumerable enumerable)
    {
        foreach (object element in enumerable)
        {
            IEnumerable candidate = element as IEnumerable;
            if (candidate != null)
            {
                foreach (object nested in candidate)
                {
                    yield return nested;
                }
            }
            else
            {
                yield return element;
            }
        }
    }
    

    Note that that will treat a string as a sequence of chars, however - you may want to special-case strings to be individual elements instead of flattening them, depending on your use case.

    Does that help?

    0 讨论(0)
  • 2020-11-27 05:12

    Basicly, you need to have a master IENumerable that is outside of your recursive function, then in your recursive function (Psuedo-code)

    private void flattenList(IEnumerable<T> list)
    {
        foreach (T item in list)
        {
            masterList.Add(item);
    
            if (item.Count > 0)
            {
                this.flattenList(item);
            }
        }
    }
    

    Though I'm really not sure what you mean by IEnumerable nested in an IEnumerable...whats within that? How many levels of nesting? Whats the final type? obviously my code isn't correct, but I hope it gets you thinking.

    0 讨论(0)
  • 2020-11-27 05:20

    Since yield is not available in VB and LINQ provides both deferred execution and a concise syntax, you can also use.

    <Extension()>
    Public Function Flatten(Of T)(ByVal objects As Generic.IEnumerable(Of T), ByVal selector As Func(Of T, Generic.IEnumerable(Of T))) As Generic.IEnumerable(Of T)
        Return objects.Union(objects.SelectMany(selector).Flatten(selector))
    End Function
    
    0 讨论(0)
  • 2020-11-27 05:21

    Here is a modified Jon Skeet's answer to allow more than "one level":

    static IEnumerable Flatten(IEnumerable enumerable)
    {
        foreach (object element in enumerable)
        {
            IEnumerable candidate = element as IEnumerable;
            if (candidate != null)
            {
                foreach (object nested in Flatten(candidate))
                {
                    yield return nested;
                }
            }
            else
            {
                yield return element;
            }
        }
    }
    

    disclaimer: I don't know C#.

    The same in Python:

    #!/usr/bin/env python
    
    def flatten(iterable):
        for item in iterable:
            if hasattr(item, '__iter__'):
                for nested in flatten(item):
                    yield nested
            else:
                yield item
    
    if __name__ == '__main__':
        for item in flatten([1,[2, 3, [[4], 5]], 6, [[[7]]], [8]]):
            print(item, end=" ")
    

    It prints:

    1 2 3 4 5 6 7 8 
    
    0 讨论(0)
  • 2020-11-27 05:21
    static class EnumerableExtensions
    {
        public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> sequence)
        {
            foreach(var child in sequence)
                foreach(var item in child)
                    yield return item;
        }
    }
    

    Maybe like this? Or do you mean that it could potentially be infintly deep?

    0 讨论(0)
提交回复
热议问题