10

I've got a String array that I'm wanting to add to a string builder by way of LINQ.

What I'm basically trying to say is "For each item in this array, append a line to this StringBuilder".

I can do this quite easily using a foreach loop however the following code doesn't seem to do anything. What am I missing?

stringArray.Select(x => stringBuilder.AppendLine(x));

Where as this works:

foreach(String item in stringArray)
{
  stringBuilder.AppendLine(item);
}
sebagomez
  • 9,501
  • 7
  • 51
  • 89
Jamie Dixon
  • 53,019
  • 19
  • 125
  • 162

4 Answers4

23

If you insist on doing it in a LINQy way:

StringBuilder builder = StringArray.Aggregate(
                            new StringBuilder(),
                            (sb, s) => sb.AppendLine(s)
                        );

Alternatively, as Luke pointed out in a comment on another post, you could say

Array.ForEach(StringArray, s => stringBuilder.AppendLine(s));

The reason that Select does not work is because Select is for projecting and creating an IEnumerable of the projection. So the line of code

StringArray.Select(s => stringBuilder.AppendLine(s))

does not iterate over the StringArray calling stringBuilder.AppendLine(s) on each iteration. Rather, it creates an IEnumerable<StringBuilder> that can be enumerated over.

I suppose that you could say

var e = stringArray.Select(x => stringBuilder.AppendLine(x));
StringBuilder sb = e.Last();
Console.WriteLine(sb.ToString());

but that is really hideous.

Community
  • 1
  • 1
jason
  • 236,483
  • 35
  • 423
  • 525
  • Thanks Jason. Your answer explains why the Select method didn't achieve the result I wanted and why the foreach was working. I'll stick with the foreach. I was really just curious about what I was missing. Now I know. – Jamie Dixon Nov 10 '09 at 14:27
  • Is Last() guaranteed to iterate? Rather than just fetch stringArray[stringArray.count-1]? I think I would have preferred ToList(); – Taemyr May 08 '17 at 14:46
8

Use the "ForEach" extension method instead of "Select".

stringArray.ToList().ForEach(x => stringBuilder.AppendLine(x));

or

Array.ForEach(stringArray, x => stringBuilder.AppendLine(x));
scottrudy
  • 1,633
  • 1
  • 14
  • 24
1

If you're using .NET core then this will work:

stringBuilder.AppendJoin(Environment.NewLine, stringArray);

Although it's not leveraging LINQ, but it does get it done in one line without adding any extra code.

crane101
  • 11
  • 3
  • 1
    You should include a clarification that this method is available only in .NET Core, at least for now. It should also be in .NET 5 when that's officially released. – Peter Duniho Sep 23 '20 at 05:36
  • This is one of the things I love about the internet. 11 years on and we have a new answer! Amazing. – Jamie Dixon Sep 24 '20 at 09:05
-1
stringArray.DoForAll(x => StringBuilder.AppendLine(x));

Where, DoForAll is an extension method:

public static class CommonExtensions 
{ 
    public static void DoForAll<T>(this IEnumerable<T> items, Action<T> action) where T: class 
    { 
        if (action == null) 
            throw new ArgumentNullException("action"); 
        foreach (var item in items) 
            action(item);   
    }
} 
Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
vladhorby
  • 358
  • 4
  • 6
  • Sorry, I've had this extension method for so long, I forgot is not part of the framework. public static class CommonExtensions { public static void DoForAll(this IEnumerable items, Action action) where T: class { if (action == null) throw new ArgumentNullException("action"); foreach (var item in items) action(item); } } – vladhorby Nov 10 '09 at 14:37
  • @vladhorby: One view on why `DoForAll` and other equivalent extensions are not implemented: http://blogs.msdn.com/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx – jason Nov 10 '09 at 15:25