views:

279

answers:

8

I'm building flat file content from collections of strings.

Example collection: A, B, C, D, E, etc.

I want to be able to output these values to a string with line feeds in one swoop with Linq if possible.

Sample Output:

A

B

C

D

E

etc.

Here's the VB.NET code that does the job currently:

For Each fieldValue As String In field.Values
   fileContent.Append(fieldValue + Environment.NewLine)
Next

I've tried a bunch of ways to get Linq to do the job, but haven't been able to find the right combination. Thoughts?

+4  A: 

Have you tried a simple join?..

if field.Values is already an array of strings then this should work fine.. otherwise you could use LINQ .ToArray() to convert the collection to an array.

string joined = string.Join(Environment.NewLine, field.Values);

VB

Dim joined As String = String.Join(Environment.NewLine, field.Values)

Just figured I would add, if you really, really just wanted to do this with LINQ a Aggregrate would work, although I wouldn't really recommend this for your needs.

field.Values.Aggregate(string.Empty, (s1, s2) => s1 += Environment.NewLine + s2);
Quintin Robinson
Very close on the Join. But it's a collection, so I had to convert it first AND add another NewLine at the end.fileContent.Append(String.Join(Environment.NewLine, field.Values.ToArray()) + Environment.NewLine)
Boydski
One shouldn't use LINQ just for the sake of using LINQ. I think your String.Join solution is way better than any LINQ solution ;-)
Meta-Knight
@Boydski I'm glad you got the solution working the way you wanted it. @Meta-Knight I agree
Quintin Robinson
A: 

If Values is a List you can do this...

field.Values.ForEach(o=>fileContent.Append(o + Environment.NewLine));
Jason Punyon
There's no "ForEach" in the API. ???
Boydski
Jason, there's no foreach in VB9.
Meta-Knight
mmm...That'll teach me. Please vote me down...
Jason Punyon
Minor mistake sir. Voting you back up for the help. At least I know what to expect in v4.0!
Boydski
+1  A: 

I created these extension methods that can be used to concatenate any number of items in a collection. It may be a bit overkill for you example, but if you need to concatenate items that are objects and not strings it can work great.

Usage:

fileContent = field.Values.Contatenate(Environment.NewLine);

Extensions:

public static class EnumerableExtensions
{
    public static string Concatenate<T>(this IEnumerable<T> source, string seperator)
    {
        return Concatenate(source, i => i.ToString(), seperator);
    }

    public static string Concatenate<T>(this IEnumerable<T> source, Func<T, string> selector, string seperator)
    {
        var builder = new StringBuilder();
        foreach (var item in source)
        {
            if (builder.Length > 0)
                builder.Append(seperator);

            builder.Append(selector(item));
        }
        return builder.ToString();
    }

    public static string ToCsv<T>(this IEnumerable<T> source)
    {
        return Concatenate(source, i => i.ToString(), ",");
    }

    public static string ToCsv<T>(this IEnumerable<T> source, Func<T, string> selector)
    {
        return Concatenate(source, selector, ",");
    }
}
bendewey
+1  A: 

You can use the Aggregate method to get there, but it won't be nearly as efficient as a foreach loop that Appends to a file or StringBuilder.

I believe your basic issue is that Linq's proper usage is to return results. The designers did not intend for you to use a Linq statement to modify the objects it is iterating through. That likely explains the general difficulty you've had with this task.

Don't forget that foreach is a proper way to use Linq results!

John Fisher
A: 

You may want to use IEnumerable.Aggregate.

Your code will look something like this:

Dim fileContentString As String = _
        field.Values.Aggregate(Function(ByVal JoinedValues, ByVal DiscreteValue) 
            JoinedValues & Environment.NewLine & DiscreteValue)

Disclaimer - Just because you CAN use Linq, doesn't mean it's the right tool for this job. As others have pointed out, a ForEach loop would be more efficient.

Cam Soper
A: 

You can use a StringBuilder in the Aggregate extension method:

Dim fileContent As String = _
   field.Values.Aggregate( _
      New StringBuilder(), _
      Function(b, i) b.AppendLine(i) _
   ).ToString()
Guffa
A: 

Create an extension method -

<Extension()> _
Public Function Flatten(ByVal stringList As IList(Of String), ByVal delimiter As String) As String
        Dim strB As New StringBuilder

        For Each Str As String In stringList
            If strB.ToString.Length > 0 Then
                strB.Append(delimiter)
            End If
            strB.Append(Str)
        Next
        Return strB.ToString
    End Function

Once you have that, you can simple do what you want like this -

Dim letters as New List(Of String)
'do your loading of the list

Dim formattedString As String = letters.Flatten(Environment.NewLine)
Dan Appleyard
+1  A: 

Note that .NET 4.0 includes a new String.Concat<T>() static method which takes an IEnumerable<T> and calls ToString on each object in the list. Should come in handy!

Matt Hamilton
I'm starting to watch the v4.0 stuff as much as I can. Thanks for the suggestion!
Boydski