views:

100

answers:

3

Currently I'm getting a list of HeaderColumns from the following XML snippet:

<PerformancePanel>
  <HeaderColumns>
    <column performanceId="12" text="Over last month %" />
    <column performanceId="13" text="Over last 3 months %" />
    <column performanceId="16" text="1 Year %" />
    <column performanceId="18" text="3 Years % p.a." />
    <column performanceId="20" text="5 Years % p.a." />
    <column performanceId="22" text="10 Years % p.a." />
  </HeaderColumns>
</PerformancePanel>

from which I create an object as follows: (admitedly similar to an earlier question!)

var performancePanels = new
{
    Panels = (from panel in doc.Elements("PerformancePanel")
            select new
            {
                HeaderColumns = (from column in panel.Elements("HeaderColumns").Elements("column")
                                 select new
                                 {
                                     PerformanceId = (int)column.Attribute("performanceId"),
                                     Text = (string)column.Attribute("text")
                                 }).ToList(),
              }).ToList()
};

I'd like if HeaderColumns was a Dictionary() so later I extract the values from the anonymous object like follows:

Dictionary<int, string> myHeaders = new Dictionary<int, string>();
foreach (var column in performancePanels.Panels[0].HeaderColumns)
{
    myHeaders.Add(column.PerformanceId, column.Text);
}

I thought I could achieve this with the Linq to XML with something similar to this

HeaderColumns = (from column in panel.Elements("HeaderColumns").Elements("column")
                 select new Dictionary<int, string>()
                 {
                     (int)column.Attribute("performanceId"),
                     (string)column.Attribute("text")
                 }).ToDictionary<int,string>(),

but this doesn't work because

  1. ToDictionary() needs a Func parameter and I don't know what that is / how to implement it, and
  2. the code's probably wrong anyway!

Could somebody please suggest how I can achieve the result I need? Thanks.

Edit: I've tried to implement Spender's solution solution below but I'm having difficulty understanding what I need to change in my code. Can somebody clarify? Thanks

+2  A: 

This is untested, but should do the trick:

panel
    .Elements("HeaderColumns")
    .Elements("column")
    .ToDictionary(
        column=>(int)column.Attribute("performanceId"),
        column=>(string)column.Attribute("text")
    )

In this case I am using the following overload of ToDictionary:

public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TSource, TElement> elementSelector
)

So, here source would be:

panel
    .Elements("HeaderColumns")
    .Elements("column")

And keySelector and elementSelector are functions (delegates) that take an element of type TSource (from source), and return a value derived from the element:

keySelector:

        column=>(int)column.Attribute("performanceId")

elementSelector:

        column=>(string)column.Attribute("text")

These are declared in lambda format. The type of the dictionary is then inferred from the return types of these delegates. This would be Dictionary<int,string> in this case.

The generic delegate Func<TSource, TKey> would define the type of a method that takes a parameter of type TSource and returns a value of type TKey. It's very important to get to grips with exactly what this means to have a productive time using the Linq extension methods.

spender
Hi Spender - thanks for this, but I'm having a little difficulty understanding what I need to do here. Any chance you could explain a little more? Thanks
DaveDev
+1  A: 

It is hard to give a complete answer as your xml and C# don't match up; but from each "HeaderColumns" you can use:

var columns = from column in headerColumns.Elements("column")
              select new {
                  PerformanceId = (int)column.Attribute("performanceId"),
                  Text = (string)column.Attribute("text")
              };
Dictionary<int, string> myHeaders = columns.ToDictionary(
    column => column.PerformanceId, column => column.Text);

If you have multiple <HeaderColumns>, then SelectMany maybe...

var columns = from panel in doc.Elements("PerformancePanel")
              from headers in panel.Elements("HeaderColumns")
              from column in headers.Elements("column")
              select new {
                  PerformanceId = (int)column.Attribute("performanceId"),
                  Text = (string)column.Attribute("text")
              };
Dictionary<int, string> myHeaders = columns.ToDictionary(
    column => column.PerformanceId, column => column.Text);
Marc Gravell
You're right Mark - I didn't realise I didn't include the full XML snippet - I've corrected it now. Thanks
DaveDev
Sorry Marc, I misspelt your name! - SO wouldn't let me edit the comment. Thanks again.
DaveDev
A: 

I found this to be a suitable answer to this question:

http://stackoverflow.com/questions/2828336/how-to-create-dictionaryint-string-via-linq-to-xml

DaveDev