3 nested groups with linq

kds6253 picture kds6253 · Mar 20, 2012 · Viewed 7k times · Source

I am trying to get 4 List deep List collection, List<List<List<List<int>>>>. From my Xml which looks like

<root> 
    <Claim key="1" carrier="carA" zip="34343" pages="1"/>
    <Claim key="2" carrier="carA" zip="34343" pages="2"/>
    <Claim key="3" carrier="carB" zip="10505" pages="2"/>
    <Claim key="4" carrier="carB" zip="10505" pages="4"/> 
    <Claim key="5" carrier="carB" zip="10505" pages="4"/>
</root>

the structure of the output should look like

-all
   -1
       -34343
           -carA
                   -1

   -2
       -34343
           -carA
                   -2

       -10505
               -carB
                   -3
   -4
       -10505
           -carB
                    -4
                    -5

the goal is to sort my XML based on the node attributes first by number of pages, then by zip, then by carrier. I will then need to cycle through the result list and process each claim in the particular order. I am having trouble getting the syntax right for 3 nested groups. I have accomplished getting 2 nested groups can anyone help me get the third.

here is my code so far.

var query = from claim in root.Elements("Claim")
                        group claim by claim.Attributes("Pages").First().Value into pageGroups
                        from zipGroups in
                            (from claim in pageGroups
                             group claim by int.Parse(claim.Attributes("CarrierZip").First().Value))
                        group zipGroups by pageGroups.Key;

Answer

leviathanbadger picture leviathanbadger · Mar 20, 2012

I'm not sure how to do this with XML, but if you've already translated the claims into an array of some type Claim (e.g., in the code, claims is of type Claim[]) and the Claim type has properties or fields called Key, Carrier, Zip, and Pages then this should work.

var dic = (from claim in claims
           group claim by claim.Pages into pageGroup
           select new {
               Page = pageGroup.Key,
               Entries =
                   (from zentry in pageGroup
                    group zentry by zentry.Zip into zipGroup
                    select new {
                        Zip = zipGroup.Key,
                        Entries =
                            (from centry in zipGroup
                             group centry by centry.Carrier into carrierGroup
                             select new { Carrier = carrierGroup.Key, Entries = carrierGroup.AsEnumerable() })
                            .ToDictionary(ent => ent.Carrier, ent => ent.Entries)
                    }).ToDictionary(ent => ent.Zip, ent => ent.Entries)
           }).ToDictionary(ent => ent.Page, ent => ent.Entries);

It's not very clean, but it works. You can select a claim with the given page, zip, and carrier like so:

var myclaim = dic[4][34343]["carB"];

I chose to give you a way to translate into Dictionary<TKey, TValue> instead of List<T> because translating to List loses the key, so the only way to get the key (the page, zip or carrier) would be to traverse the list down which could get ugly and complicated. Sorry if a Dictionary won't work for you.