JimmyHu
JimmyHu

Reputation: 519

C# Arbitrarily Nested IEnumerable object

I am trying to write some methods to deal with multi-layered IEnumerable object in C#. The following example is the method named "ConsoleWrite" which used for printing each string in IEnumerable object to console.

public void ConsoleWrite(IEnumerable<string> StringData)
{
    foreach (var EachString in StringData)
    {
        Console.Write(EachString);
    }
}

The usage of "ConsoleWrite" method.

List<string> Test1 = new List<string>();
Test1.Add("123");
Test1.Add("456");
ConsoleWrite(Test1);

It works well and the output is "123456". However, when it comes to the List<List<string>> case, this ConsoleWrite method needs to be overloading as below.

public void ConsoleWrite(IEnumerable<IEnumerable<string>> StringData)
{
    foreach (var EachString in StringData)
    {
        ConsoleWrite(EachString);
    }
}

The test of the case List<List<string>>.

List<List<string>> Test2 = new List<List<string>>();
Test2.Add(Test1);
Test2.Add(Test1);
ConsoleWrite(Test2)

Furthermore, for the case List<List<List<string>>>, this ConsoleWrite method needs to be overloading as public void ConsoleWrite(IEnumerable<IEnumerable<IEnumerable<string>>> StringData). I am wondering that is there any way to deal with arbitrarily nested List IEnumerable<...<IEnumerable<string>>> easily and without overloading one-by-one?

Any suggestions are welcome.

Upvotes: 1

Views: 362

Answers (1)

TheGeneral
TheGeneral

Reputation: 81553

You could just the non generic IEnumerable, some pattern matching and recursion

public static void ConsoleWrite(IEnumerable source)
{
   foreach (var item in source)
      if (item is string str)
         Console.WriteLine(str);
      else if (item is IEnumerable enumerable)
         ConsoleWrite(enumerable);
}

Example

var list = new List<string> {"1", "2", "3"};

ConsoleWrite(list);

var list2 = new List<List<string>>
   {
      new List<string>(){"4", "5", "6"},
      new List<string>(){"7", "8", "9"}
   };

ConsoleWrite(list2);

Output

1
2
3
4
5
6
7
8
9

Full Demo here


Another more generic way would be to create an extension method, Like SelectMany with recursion

public static class Extensions
{
   public static IEnumerable<T> FlattenMany<T>(this IEnumerable source)
   {
      foreach (var item in source)
         if (item is T t)
            yield return t;
         else if (item is IEnumerable enumerable)
            foreach (var elem in enumerable.FlattenMany<T>())
               yield return elem;
   }
}

Usage

var list2 = new List<List<string>>
{
   new List<string>() {"4", "5", "6"},
   new List<string>() {"7", "8", "9"}
};

var results = list2.FlattenMany<string>();

Upvotes: 3

Related Questions