Sunday, August 7, 2011

The difference between SelectMany and Select in LINQ

This is follow up on my previous blog post:Adding xml strings in C# using LINQ.

In the previous post I showed that LINQ can be used to to manipulate XElements and strings. LINQ managed the transformation between the classes.

XElement result =
from x1 in el1
from x2 in el2
select x1.Substring(0, x1.LastIndexOf("<")) + x2.Substring(x2.IndexOf(">") + 1);

el1, el2 and the result are XElements and x1, x2 and the object before select are strings.


In the example there are two XElements and the compiler requires that we implement SelectMany. If delete SelectMany we get the following error:


SelectMany


In case we have one XElement we expect that the following will compile:

private static XElement ToUpper(XElement el)
{
XElement result =
from x in el
select x.ToUpper();

return result;
}

It does not: “'System.Xml.Linq.XElement' does not contain a definition for 'Select'”


So we implement Select:

public static XElement Select(this XElement el, Func<string, XElement> f)
{
return SelectMany(el, f, (x, y) => y);
}

Again it does not compile: “Cannot implicitly convert type 'string' to 'System.Xml.Linq.XElement'”


So we add an extra conversion:

private static XElement ToUpper(XElement el)
{
XElement result =
from x in el
select XElement.Parse(x.ToUpper());

return result;
}

This time it works:

var el = XElement.Parse("<a><b>1</b><b>2</b></a>");
Console.WriteLine(string.Format("ToUpper: {0}", ToUpper(el)));
Results in:
ToUpper

as expected.


Another way to solve the issue is to add a dummy line and use SelectMany:

private static XElement ToUpper(XElement el)
{
XElement result =
from x in el
from dummy in XElement.Parse("<dummy></dummy>")
select x.ToUpper();

return result;
}

with the same result.


Update: more information at: http://msdn.microsoft.com/en-us/library/bb546168.aspx

No comments:

Total Pageviews