views:

123

answers:

5

What is a shorthand way to String.Join a non-string array as in the second example?

string[] names = { "Joe", "Roger", "John" };
Console.WriteLine("the names are {0}", String.Join(", ", names)); //ok

decimal[] prices = { 39.99M, 29.99m, 29.99m, 19.99m, 49.99m };
Console.WriteLine("the prices are {0}", String.Join(", ", prices)); //bad overload
+11  A: 

If you have LINQ:

decimal[] prices = { 39.99M, 29.99m, 29.99m, 19.99m, 49.99m }; 
Console.WriteLine("the prices are {0}", 
    String.Join(", ", 
       prices.Select(p => p.ToString()).ToArray()
    )
);
ck
Note that you'll need to call `ToArray()` if you're not using .NET 4.
Jon Skeet
@Jon - duly noted...
ck
I'd +1 except for the fact that this creates a second array, which is fine for the trivial example given, but is a bad real world habit to get into.
Binary Worrier
If you're using .NET 4, you don't need LINQ at all. `string.Join(", ", prices);`
Marc
+4  A: 
Console.WriteLine("the prices are {0}", String.Join(", ", Array.ConvertAll(prices, p => p.ToString()));
onof
A: 

Could use linq to translate to strings:

Console.WriteLine("the prices are {0}", String.Join(", ", prices.Select(p => p.ToString()).ToArray()));

or use Aggregate() instead of string.Join()

Console.WriteLine("the prices are {0}",
    prices.Select(p => p.ToString()).
          .Aggregate((total, p) => total + ", " + p));

or even (with slightly different formatting)

Console.WriteLine(
    prices.Select(p => p.ToString()).
          .Aggregate("the prices are", (total, p) => total + ", " + p));
Andrew
+1  A: 

Picking up where ck left off, extract it to a method to make it reusable:

public static class EnumerableExtensions {
  public static string Join<T>(this IEnumerable<T> self, string separator) { 
    if (self == null) throw new ArgumentNullException();
    if (separator == null) throw new ArgumentNullException("separator");
    return String.Join(separator, self.Select(e => e.ToString()).ToArray());
  }
}

Now the usage is more readable:

Console.WriteLine("the prices are {0}", prices.Join(", "));
Jordão
[Don't throw `NullReferenceException` when validating `this` in an extension method](http://blogs.msdn.com/b/jaredpar/archive/2010/06/28/do-not-throw-a-nullreferenceexception-when-validing-this-in-an-extension-method.aspx)
hmemcpy
@hmemcpy: Thanks, it makes sense.
Jordão
+2  A: 

I have two Delimit functions that do this for me.
They've a very light footprint, in that no second array is created, and where a string is returned, a StringBuilder is used under the covers, so it's not copying and concatenating to the same string causing ever longer concatenation delays.
As such they are useful for series of very large and/or unknown length.

The frist writes to a TextWriter and returns nothing, the second returns a string, and delegates to the first passing in a StringWriter.

public static void Delimit<T>(this IEnumerable<T> me, System.IO.TextWriter writer, string delimiter)
{
    var iter = me.GetEnumerator();
    if (iter.MoveNext())
        writer.Write(iter.Current.ToString());

    while (iter.MoveNext())
    {
        writer.Write(delimiter);
        writer.Write(iter.Current.ToString());
    }
}

public static string Delimit<T>(this IEnumerable<T> me, string delimiter)
{
    var writer = new System.IO.StringWriter();
    me.Delimit(writer, delimiter);
    return writer.ToString();
}

So given prices above you'd have

decimal[] prices = { 39.99M, 29.99m, 29.99m, 19.99m, 49.99m }; 
Console.WriteLine("the prices are {0}", prices.Delimit(", "));

or

decimal[] prices = { 39.99M, 29.99m, 29.99m, 19.99m, 49.99m }; 
Console.Write("the prices are ")
prices.Delimit(System.Console.Out, ", ");
Console.WriteLine();
Binary Worrier
Is there much overhead in creating a StringWriter as opposed to an Array? I have a similar function for creating delimited strings that adds in text qualifiers for creating CSVs.
ck
CK: For small arrays, probably not (the only way to know for sure is to profile them). The problem is with very large arrays, or sequences of unknown size. Also, if you've a list of decimal values, are you going to call .ToArray on the list first? At some point you're container needs to be copied to an Array for .Join to work. For me the joy of linq is that I can stop thinking about different collection types and what they contain, and just write generic code with minimum overhead.
Binary Worrier