tags:

views:

2558

answers:

3

How to create a nested group by query in LINQ for the below table:

Code Mktcode Id
1 10 0001 
2 20 0010 
1 10 0012 
1 20 0010 
1 20 0014 
2 20 0001
2 30 0002
1 30 0002
1 30 0005

I want to get the above and convert it to the data structure of the sort Dictionary< Code, List<Dictionary< Mktcode, List<Id>>>>.

So the result of this dictionary would be

{1, ({10,(0001,0002)}, {20,(0010,0014)}, {30, (0002, 0005)})},
{2, ({20,(0001, 0010)}, {30, (0020)} )}
+2  A: 

You can build lookups (Kinds of Dictionary<,List<>>) using group by into

var lines = new []
{
    new {Code = 1, MktCode = 10, Id = 1},
    new {Code = 2, MktCode = 20, Id = 10},
    new {Code = 1, MktCode = 10, Id = 12},
    new {Code = 1, MktCode = 20, Id = 10},
    new {Code = 1, MktCode = 20, Id = 14},
    new {Code = 2, MktCode = 20, Id = 1},
    new {Code = 2, MktCode = 30, Id = 2},
    new {Code = 1, MktCode = 30, Id = 2},
    new {Code = 1, MktCode = 30, Id = 5},
};

var groups = from line in lines
    group line by line.Code
    into codeGroup
    select new
    {
        Code = codeGroup.Key,
        Items = from l in codeGroup
            group l by l.MktCode into mktCodeGroup
            select new
            {
                MktCode = mktCodeGroup.Key, 
                Ids = from mktLine in mktCodeGroup
                    select mktLine.Id
            }
    };
Think Before Coding
Any ideas on how to convert groups to dictionary type?
Ngu Soon Hui
groups.ToDictionary(g => g.Key, g => g.ToList())
David B
+5  A: 

I'd think of it this way:

  • You're primarily grouping by code, so do that first
  • For each group, you've still got a list of results - so apply another grouping there.

Something like:

var groupedByCode = source.GroupBy(x => x.Code);

var groupedByCodeAndThenId = groupedByCode.Select(group =>
    new { Key=group.Key, NestedGroup = group.ToLookup
                                         (result => result.MktCode, result => result.Id));

var dictionary = groupedByCodeAndThenId.ToDictionary
    (result => result.Key, result => result.NestedGroup);

That will give you a Dictionary<Code, Lookup<MktCode, Id>> - I think that's what you want. It's completely untested though.

Jon Skeet
Geez, you are soo right. Your mental power is simply unbelievable.
Ngu Soon Hui
+1  A: 

Here's how I'd do it:

Dictionary<Code, Dictionary<MktCode, List<Id>>> myStructure =
  myList
    .GroupBy(e => e.Code)
    .ToDictionary(
      g => g.Key,
      g => g
        .GroupBy(e => e.Mktcode)
        .ToDictionary(
          g2 => g2.Key,
          g2 => g2.Select(e => e.Id).ToList()
        )
   )

Here's the breakdown of the process:

Group the elements by Code, and create an outer dictionary where the key is that code.

  myList
    .GroupBy(e => e.Code)
    .ToDictionary(
      g => g.Key,

For each of the keys in the outer dictionary, regroup the elements by Mktcode and create an inner dictionary.

      g => g
        .GroupBy(e => e.Mktcode)
        .ToDictionary(
          g2 => g2.Key,

For each of the keys in the inner dictionary, project the id of those elements and convert that to a list.

          g2 => g2.Select(e => e.Id).ToList()
        )
   )
David B