tags:

views:

199

answers:

4

I'm triyng to do a quick Haskell-like aggregation in Linq (C#), to turn a List into a string in the format "i^j^k..." etc.

is this possible in one query, or should I just do it the old-fasioned

foreach (int i in list)
{
     string+= i + "^"
}

(p.s. yes, that's pseudocode and wouldn't compile.)

+15  A: 

Use string.Join:

string.Join("^", list.Select(x => x.ToString()).ToArray())

In this particular case it may be more efficient to use StringBuilder directly as Append(int) may avoid creating the temporary strings. Unless this turns out to be a bottleneck, however, I'd stick to this simple single expression.

Jon Skeet
A fine answer, as Join uses a StringBuilder under the hood.
Steven Sudit
A good answer, but I was actually looking for the functional-style syntax!
Ed Woodcock
What's not functional about it? You *could* use Aggregate if you really wanted to, but it would be uglier and perform worse.
Jon Skeet
I was specifically looking for the linq implementation using aggregate, you original answer was rather less functional, and that's when I posted my comment.
Ed Woodcock
Again, I don't see it as non-functional. It doesn't mutate anything, it just projects the list of ints into a list of strings, projects that to an array, and then uses a standard library method - which again, has no side effects. The solution is simpler to understand, IMO - and highlights how it's worth knowing the simplest/best ways to do things in your target platform instead of hanging on to idioms from other platforms.
Jon Skeet
A: 

It's much more efficient to use a StringBuilder instance here.

Steven Sudit
The list is going to have around 10 things in it. It's really not, stringbuilders are only more efficient by a small factor when using a small amount of variables. If it had a few thousand, yes.
Ed Woodcock
sorry, that's true, but it's still not a relevant answer as I'm basically concatenating together 1 2 3 4 and 5, not huge amounts of text.
Ed Woodcock
That's not entirely true. The size of the strings being concatenated is important also, not just the number of times the loop is firing.
Kevin
@Kevin: That's correct. Each concatenation creates a new string, requiring that the entirety of the old string be copied. In contrast, StringBuilder only needs to recopy when it has to expand its internal buffer, which can be limited by initializing with a capacity and which self-limits (I believe) by doubling the size when expanding. When ToString is called, it may need to copy one more time, depending.
Steven Sudit
@Ed: For sufficiently small quantities, it would be hard to find a method that will cause a measurable performance hit. Having said that, I don't feel comfortable intentionally writing inefficient code when the simple and efficient option of Join is available.
Steven Sudit
@Ed: Furthermore, the encapsulation of Join allows for an improved implementation in a future version of the framework. Join is a metaphor for the action that is being performed and is a very logical way to express the intention of joining strings.
Stuart Thompson
+1  A: 

It's definitely possible in LINQ (see other answer for syntax).

However, you might consider using a StringBuilder instead.

StringBuilder sb = new StringBuilder();
foreach (int i in list) sb.Append(i.ToString());

In you case, better is:

String.Join("^", list.ToArray());

that uses StringBuilder to accomplish the same work.

Stuart Thompson
+1  A: 

You can use the Aggregate extension:

string s = list.Aggregate<int, string>(String.Empty, (x, y) => (x.Length > 0 ? x + "^" : x) + y.ToString());
Guffa
While it may be elegant in some sense, the performance will be poor. I endorse Jon's answer here.
Steven Sudit
A fair enough point, however I am only concatenating at most a few sub-1000 integers, that kind of micro-optimisation is not needed!Also, I was interested in the syntax, I'm used to Haskell for functional stuff, so Linq sometimes confuses me!
Ed Woodcock
+1 for getting it all working - but I still don't think it's as simple/readable as using the built-in Join method :) You can get away without the final `ToString()` call if you want to make it shorter, although obviously it'll still be called really.
Jon Skeet
Yes, a String.Join or a StringBuilder performs better, the aggregate performs the same as the pseudo code in the question. Regarding the ToString, it does save a boxing to call it explicity instead of having the String.Concat method calling it.
Guffa
@Guffa: Just to clarify, I think you're saying that, since the compiler can't match on +(string,string), it upcasts to +(string,object), then calls ToString on the object. Right?
Steven Sudit
@Steven: The + operator for strings compiles into String.Concat calls. If all operators are strings, String.Concat(params string, string) is called, otherwise String.Concat(object, object) is called.
Guffa
@Guffa: Thanks for the clarification. There's a similar optimization for String.Format, where there are a few overloads that takes strings, plus one that takes a params object[].
Steven Sudit
@Steven: There are no overloads for String.Format that takes anything other than Object for the values. You must be thinking of some other method.
Guffa
@Guffa: You are correct. Now I'm going to kill myself trying to remember what that method was.
Steven Sudit