🌲

【LINQ】Flatten() メソッド※

に公開

※更新

LINQ なのに IEnumerable<T> の拡張メソッドじゃないのはおかしい、と。

結果ツリーのノード側に要件が追加されちゃいましたが node.Flatten() で簡単に呼べるようになりました。

ソースコード

実行環境: https://dotnetfiddle.net/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace X
{
    class Node : IEnumerable<Node>  // node implements recursion interface
    {
        public Node? Parent;
        public List<Node> Children = new();

        public IEnumerator<Node> GetEnumerator() => Children.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

        public void Add(Node node) => Children.Add(node);
    }

    static class FlattenLinqExtensions
    {
        public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source, FlattenStrategy strategy = FlattenStrategy.DepthFirst)
            where T : IEnumerable<T>
        {
            if (source is T self)
            {
                yield return self;
            }

            foreach (var child in source)
            {
                foreach (var x in child.Flatten(strategy))
                {
                    yield return x;
                }
            }
        }
    }

    enum FlattenStrategy
    {
        DepthFirst,
        BreadthFirst,
    }

    class Program
    {
        static void Main()
        {
            var node = new Node()
            {
                new Node()
                {
                    new Node(),
                    new Node(),
                },
                new Node()
                {
                    new Node(),
                    new Node(),
                },
                new Node()
                {
                    new Node(),
                    new Node(),
                },
            };

            var allNodes = node.Flatten();

            int i = 0;
            foreach (var n in node.Flatten())
            {
                i++;
                Console.WriteLine($"{i}: {n.GetHashCode()}");
            }
        }
    }
}

ノード側の実装が弄れない時用に、初版のメソッドを静的メソッドとして

IEnumerable<T> Enumerable.Flatten(T source, Func<T, IEnumerable<T>> selectMany);

にすれば完璧では?

初版

ツリー構造をフラットにするために SelectMany でアレコレやってたんですが、ふと思いつきました。

https://x.com/sator_imaging/status/1942482759420887224

👇 勢いで Issue まで建てました。標準ライブラリにあったら良いなと思ったら、ぜひ Upvote を!

https://github.com/dotnet/runtime/issues/117409

static IEnumerable<T> Flatten<T>(this T source, Func<T, IEnumerable<T>> selectMany)
{
    yield return node;

    foreach (var child in selectMany.Invoke(node))
    {
        foreach (var x in Flatten(child, selectMany))
        {
            yield return x;
        }
    }
}

以上です。お疲れ様でした。

Discussion