125

Is there a way to take a List and convert it into a comma separated string?

I know I can just loop and build it, but somehow I think some of you guys a more cool way of doing it?

I really want to learn these types of 'tricks', so please explain or link to the docs on the method you use.

dmr
  • 19,157
  • 34
  • 91
  • 136
mrblah
  • 88,033
  • 134
  • 292
  • 404

8 Answers8

201
List<int> list = ...;
string.Join(",", list.Select(n => n.ToString()).ToArray())
Pavel Minaev
  • 94,882
  • 25
  • 209
  • 280
  • 7
    Clever but slow and bloated, as it allocates one string per element. Using a StringBuilder would be much more efficient. – Steven Sudit Oct 06 '09 at 23:49
  • 3
    From what I saw online(quick search) String.Join is faster than using a StringBuilder. – Yuriy Faktorovich Oct 06 '09 at 23:51
  • 4
    http://stackoverflow.com/questions/585860/string-join-vs-stringbuilder-which-is-faster, you are incorrect Steven – Yuriy Faktorovich Oct 06 '09 at 23:53
  • 6
    I think Steven is referring to the n.ToString() part rather than the String.Join. – Larsenal Oct 06 '09 at 23:53
  • 10
    Larsenal: but StringBuilder.Append(Int32) internally calls ToString on the integer anyway. StringBuilder doesn't magically avoid the cost of allocating a string for each element; it just tucks it nicely out of sight. – itowlson Oct 07 '09 at 00:03
  • @itowlson: Good point, although it still has to keep expanding the array, which means allocating a new one and copying it over. – Steven Sudit Oct 07 '09 at 00:21
  • @Adam: Even taking itowlson's point, it remains the case that a loop with StringBuilder is going to be faster. – Steven Sudit Oct 07 '09 at 00:22
  • 1
    @Steven: Read the link, StringBuilder isn't going to be faster. – Yuriy Faktorovich Oct 07 '09 at 01:27
  • @Yuriy, I read the link, including the part where Skeet says "if you already have an array of strings to concatenate together (with a delimiter), String.Join is the fastest way of doing it." That "if" is the issue. While Join is well-optimized and even bypasses some of the overhead of StringBuilder, creating the array of strings in the first place has its own costs, including the need to copy over with each doubling. Which one is faster in practice is going to depend heavily on whether it's just a couple of numbers or many. From what I see, the latter should favor StringBuilder. – Steven Sudit Oct 07 '09 at 13:34
  • 1
    @Steven: I'm getting strange benchmarking results. I used cdiggins method for the StringBuilder, unless you want to write your own. It turned out for larger arrays the StringBuilder wins, but for shorter ones it depends on how many times it is run(LINQ wins for shorter arrays run many times). But altogether it looks like you are correct in this case the StringBuilder worked faster. – Yuriy Faktorovich Oct 07 '09 at 14:05
  • @Yuriy: That's interesting. One possibility is what I said above regarding array doubling, but then again, cdiggins suffers from the same problem on the StringBuilder side. Maybe String.Join's optimization of precalculating total length runs afoul of cache limitations. Either way, I was clearly wrong in the first place about StringBuilder being "much" faster, since I didn't take into account the unnecessary temporary string inside StringBuilder.Append(int). The two methods are both acceptable, although a properly-tuned StringBuilder.Append solution has a slight advantage on large arrays. – Steven Sudit Oct 07 '09 at 17:36
  • Fortunately, there is no more guessing now since we've got the [source](http://referencesource.microsoft.com/#mscorlib/system/text/stringbuilder.cs). – yazanpro Mar 04 '16 at 20:30
  • What about StringBuilder.AppendFormat? – Stewart Apr 07 '16 at 14:50
  • I just thought it would be worth mentioning StringBuilder implements ISerializable, which allows for the object to act as a buffer when calling ToString(), which will be slightly slower but less memory intensive than string concatenation. Even with that in mind, when compiling for release mode (or generally having optimize code set to true), a StringBuilder object is used in its stead anyway (context dependent) to avoid the unnecessary overhead. – Anthony Mason May 10 '17 at 12:30
  • Why is everyone so focused on micro optimizations – johnny 5 Oct 18 '17 at 16:40
  • 1
    @johnny5 because o(n2) sometimes turns micro into macro when large n becomes involved ... – Ben McIntyre Mar 15 '19 at 00:47
  • @BenMcintyre what does o(n^2) have to do with using string builder? If your code is slow look for macro optimizations not micro – johnny 5 Mar 15 '19 at 01:30
122

Simple solution is

List<int> list = new List<int>() {1,2,3};
string.Join<int>(",", list)

I used it just now in my code, working funtastic.

Nathan Prather
  • 1,968
  • 1
  • 16
  • 14
Nitin Daware
  • 1,233
  • 1
  • 8
  • 5
11
List<int> list = new List<int> { 1, 2, 3 };
Console.WriteLine(String.Join(",", list.Select(i => i.ToString()).ToArray()));
Yuriy Faktorovich
  • 62,163
  • 14
  • 99
  • 133
6

For approximately one gazillion solutions to a slightly more complicated version of this problem -- many of which are slow, buggy, or don't even compile -- see the comments to my article on this subject:

https://docs.microsoft.com/en-us/archive/blogs/ericlippert/comma-quibbling

and the StackOverflow commentary:

Eric Lippert's challenge "comma-quibbling", best answer?

Christian
  • 2,735
  • 4
  • 28
  • 32
Eric Lippert
  • 612,321
  • 166
  • 1,175
  • 2,033
4

For extra coolness I would make this an extension method on IEnumerable<T> so that it works on any IEnumerable:

public static class IEnumerableExtensions {
  public static string BuildString<T>(this IEnumerable<T> self, string delim = ",") {
    return string.Join(delim, self)        
  }
}

Use it as follows:

List<int> list = new List<int> { 1, 2, 3 };
Console.WriteLine(list.BuildString(", "));
cdiggins
  • 15,532
  • 6
  • 92
  • 95
  • Two possible optimizations: 1) Append the delimeter after each item regardless, then remove the extra one after the loop ends. 2) Specify a capacity for the StringBuilder. – Steven Sudit Oct 07 '09 at 01:05
  • 1
    If you dig out Reflector, it turns out that Join sums up the lengths to precalculate the buffer size, and also "primes the pump" by appending the first string outside the loop, and then, inside the loop, unconditionally appending the delimiter before the next string. Combined with some unsafe/internal tricks, it should be very fast. – Steven Sudit Oct 07 '09 at 13:36
  • @Steven: followed your advice. – cdiggins Oct 19 '09 at 16:43
  • 1
    You hardcode the delimiter in your extension and ignore the passed in value for a delimiter, and missed the semicolon. It should be `return string.Join(delim, self);` – Andrew Jul 24 '19 at 01:41
1

My "clever" entry:

        List<int> list = new List<int> { 1, 2, 3 };
        StringBuilder sb = new StringBuilder();
        var y = list.Skip(1).Aggregate(sb.Append(x.ToString()),
                    (sb1, x) =>  sb1.AppendFormat(",{0}",x));

        // A lot of mess to remove initial comma
        Console.WriteLine(y.ToString().Substring(1,y.Length - 1));

Just haven't figured how to conditionally add the comma.

Larsenal
  • 45,294
  • 40
  • 140
  • 210
  • 1
    Please don't write `Select` with side effects in the lambda. In this case you aren't even using `y`, so your `Select` is essentially just a `foreach` - so write it as such. – Pavel Minaev Oct 07 '09 at 00:05
  • I wasn't suggesting this as a good solution. OP wanted something more interesting than foreach. – Larsenal Oct 07 '09 at 00:07
  • Yeah, but abusing `Select` as `foreach` goes past "interesting" and into, well, "abuse". A more interesting approach here would be to use `Enumerable.Aggregate` with `StringBuilder` as a seed value - try that. – Pavel Minaev Oct 07 '09 at 00:09
  • Good idea. I have to step out, but I may give that a whirl. – Larsenal Oct 07 '09 at 00:10
1

Seems reasonablly fast.

IList<int> listItem = Enumerable.Range(0, 100000).ToList();
var result = listItem.Aggregate<int, StringBuilder, string>(new StringBuilder(), (strBuild, intVal) => { strBuild.Append(intVal); strBuild.Append(","); return strBuild; }, (strBuild) => strBuild.ToString(0, strBuild.Length - 1));
Gregory
  • 501
  • 6
  • 13
0

you can use, the System.Linq library; It is more efficient:

using System.Linq;
string str =string.Join(",", MyList.Select(x => x.NombreAtributo));
Maurico Bello
  • 367
  • 2
  • 10