tags:

views:

72

answers:

4

Say I have a List of objects, and the object has a string property. I want to get a single comma-separated list of the value of each string property of each object in the list. Here's 1 way to do it (sans linq)


StringBuilder result = new StringBuilder()
foreach(myObject obj in myList)
{
  result.Append(obj.TheString);
  result.Append(", ");
}
// then trim the trailing ", " and call ToString() on result, etc, etc...

Here's my first shot at linqification. Is there a better way?


string result = string.Join(", ", myList.Select(myObj => myObj.TheString).ToArray());

That's one line of code, but it doesn't look very efficient to me -- iterate the list just to build an array, just to iterate the array and build a string... whew!

Is there a better way?

+5  A: 

If you want efficient, use Enumerable.Aggregate with StringBuilder:

string result = myList.Aggregate(new StringBuilder(),
                                 (sb, o) => sb.Append(o.TheString).Append(", "))
                      .ToString();

The original problem is that String.Join wants an array. In .NET 4, there will be an overload that takes IEnumerable<string> (and I expect it will be implemented like above).

Pavel Minaev
+1 Really enjoy answers like this that show what Linq can do ! It fascinates me you can set the type of 'result to StringBuilder (of course after removing .ToString()) and set a breakpoint, and find Linq evaluation has been done before you convert to string ... which adds to my confusion about so-called 'deferred evaluation' in Linq ... but that's not your problem ! :) I wrapped your solution up in a method, and then used StringBuilder.Remove to get rid of the final extra space and comma before converting to string. Will experiment with making this into an extension method. Thanks !
BillW
The deferred evaluation is deferred to produce `StringBuilder`, but `StringBuilder` itself isn't deferred - though nothing precludes one from writing a lazy `StringBuilder` as well (and in theory it's quite possible to have basic strings lazy too, though that would need CLR support).
Pavel Minaev
+1  A: 

I like this extension method for joining strings. It's basically the same technique you are using, but wrapped in an extension method. I wouldn't recommend it for large sets since efficiency was not the goal. The benefit to me is expressiveness (very linqy) and convenient for small sets:

[Test]
public void Should_make_comma_delimited_list()
{
    var colors = new List<HasColor>
    {
        new HasColor { Color = "red" },
        new HasColor { Color = "green" },
        new HasColor { Color = "blue" }
    };

    var result = colors.Implode(x => x.Color, ", ");

    Assert.That(result, Is.EqualTo("red, green, blue"));
}

public class HasColor
{
    public string Color { get; set; }
}

public static class LinqExtensions
{
    public static string Implode<T>(this IEnumerable<T> list, Func<T, string> func, string separator)
    {
        return string.Join(separator, list.Select(func).ToArray());
    }
}
Michael Valenty
+1 Appreciated !
BillW
A: 

Here's another way (result is a StringBuilder):

myList.ForEach(
    myObj => 
    { 
        if (result.Length != 0) 
            result.Append(","); 
        result.Append(myObj.TheString); 
    }
    );
Jon Sagara
+1  A: 

Use string.Join, it's good enough.

Optimize when profiler will tell you to do so.

Readability of the version with StringBuilder is poor and you are still getting your trailing comma.

Konstantin Spirin