views:

155

answers:

4

I've used this in the past to build comma seperated lists:

var list = new List<int>{1,2,3};
var retVal = list.Select(i=>i.ToString()).Aggregate((a,b) => a+", "+b);

Works great.

I'm trying to do the same sort of thing to 'wrap' each element as an xml node.

Something like:

Aggregate((a, b) => string.Format("<ID>{0}</ID><ID>{1}</ID>", a,b))

Can't quite seem to make it work though. Is it possible? Or is there an easier way entirely?

Thanks.

+2  A: 

Shouldn't it be like:

Aggregate((a, b) => string.Format("{0}<ID>{1}</ID>", a,b))

You're adding to a new nodes.

Blindy
+2  A: 

Blindy's answer will certainly work, but I'd personally use:

var list = new List<int> {1, 2, 3};
var joined = string.Join("", list.Select(x => "<ID>" + x + "</ID>")
                                 .ToArray());

I personally find that easier to read and understand than the aggregation. I believe it will be more efficient, too - although that will only matter for large lists.

Jon Skeet
+1  A: 

Or is there an easier way entirely?

List<int> list = new List<int>{1, 2, 3};
var xmlNodes = list.Select(i => new XElement("ID", i));
XElement xml = new XElement("Data", xmlNodes);
Console.WriteLine(xml);
David B
A: 

There are a number of ways to achieve this but I suspect the correct answer is "it depends". Your original example for creating a CSV string uses the string concatenation operator; the recommended approach is to use the StringBuilder class for this purpose. And in .Net 4.0 there is a new overload for the string.Join() method that is a whole lot simpler to use and understand.

// .Net 3.5
var list = new List<int>{1,2,3};
var csv = list.Aggregate(new StringBuilder(), 
    (sb, i) => sb.Append(i).Append(','),
    sb => { if (sb.Length > 0) sb.Length--; return sb.ToString(); });

// .Net 4.0
var csv1 = string.Join(",", list);

If your intention is to create an XML Document rather than a string then David B's answer above is a good option:

var xml = new XElement("Root", list.Select(i => new XElement("ID", i)));
// <Root>
//   <ID>1</ID>
//   <ID>2</ID>
//   <ID>3</ID>
// </Root> 

For creating XML strings I prefer to avoid explicitly coding opening and closing tags. In your example it would be difficult to get the element name "ID" incorrect in either opening or closing tags but I think of this in terms of the DRY principle. On occasion I have forgotten to modify the closing tag for an element when I have modified the opening tag e.g. in config files. Using XElement avoids this issue completely:

// .Net 3.5
var xml1 = list.Aggregate(new StringBuilder(), 
           (sb, i) => sb.Append(new XElement("ID", i)),
           sb => sb.ToString());

// .Net 4.0
var xml2 = string.Join("", list.Select (i => new XElement("ID", i)));
// both xml1 & xml2 contain "<ID>1</ID><ID>2</ID><ID>3</ID>"

Performance of Aggregate() versus string.Join(), string.Join() wins every time (with the fairly limited/basic test case I used).

David Clarke