Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concat all strings inside a List<List<string>> using LINQ

Tags:

c#

.net

linq

My question is almost same as this one, but the List dimension is n. How to concat all strings inside a List<List<List...<string>> (n dimension list) using LINQ ?


NOTE: Interested for both cases, n is known or unknown

like image 210
AntonIva Avatar asked Feb 09 '23 12:02

AntonIva


2 Answers

Since the linked question is tagged as c# so i add this answer with c# code.

If the number of nested lists are known You have to use SelectMany() over and over to unwrap all the nested lists to sequence of chars. then make string out of that sequence.

List<List<List<string>>> nestedList = new List<List<List<string>>>();
var result = new string(nestedList.SelectMany(x => x).SelectMany(x => x).SelectMany(x => x).ToArray());

If the number of nested lists are not known you have to use reflection, since the type is not known. I didnt used reflection directly but actually dynamic type does. The performance would be terrible here of course ;) but it does what you want.

using Microsoft.CSharp.RuntimeBinder;

//...

private static string ConcatAll<T>(T nestedList) where T : IList
{
    dynamic templist = nestedList;
    try
    {
        while (true)
        {
            List<dynamic> inner = new List<dynamic>(templist).SelectMany<dynamic, dynamic>(x => x).ToList();
            templist = inner;
        }
    }
    catch (RuntimeBinderException)
    {
        List<object> l = templist;
        return l.Aggregate("", (a, b) => a + b);
    }
}

Here is the test

private static void Main(string[] args)
{
    List<List<List<string>>> nestedList = new List<List<List<string>>>
    {
        new List<List<string>> {new List<string> {"Hello "}, new List<string> {"World "}},
        new List<List<string>> {new List<string> {"Goodbye "}, new List<string> {"World ", "End "}}
    };

    Console.WriteLine(ConcatAll(nestedList));
}

Outputs:

Hello World Goodbye World End

Update:

After a bit fiddling i ended up this implementation. maybe better without try catch.

private static string ConcatAll<T>(T nestedList) where T : IList
{
    dynamic templist = nestedList;
    while (templist.Count > 0 && !(templist[0] is char?))
    {
        List<dynamic> inner = new List<dynamic>(templist).SelectMany<dynamic, dynamic>(x =>
        {
            var s = x as string;
            if (s != null)
            {
                return s.Cast<dynamic>();
            }
            return x;
        }).ToList();
        templist = inner;
    }
    return new string(((List<object>) templist).Cast<char>().ToArray());
}
like image 127
M.kazem Akhgary Avatar answered Feb 11 '23 02:02

M.kazem Akhgary


Another solution could be using a recursive method to flatten all your lists:

 static IEnumerable<string> Flatten(IEnumerable enumerable)
 {
        foreach (object el in enumerable)
        {
            if (enumerable is IEnumerable<string>)
            {
                yield return (string) el;
            }
            else
            {
                IEnumerable candidate = el as IEnumerable;
                if (candidate != null)
                {
                    foreach (string nested in Flatten(candidate))
                    {
                        yield return nested;
                    }
                }
            }
        }
 }

With this method you can concat all the strings this way:

 List<List<List<string>>> nestedList = new List<List<List<string>>>
                                       {
                                          new List<List<string>> {new List<string> {"Hello "}, new List<string> {"World "}},
                                          new List<List<string>> {new List<string> {"Goodbye "}, new List<string> {"World ", "End "}}
                                      };

Console.WriteLine(String.Join(" ",Flatten(nestedList))); 

This idea was taken from this post.

like image 39
octavioccl Avatar answered Feb 11 '23 01:02

octavioccl