24

How would I create a nested grouping for the table below, using LINQ? I want to group by Code, then by Mktcode.

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'd like a dictionary, in the end, like

Dictionary<Code, List<Dictionary<Mktcode, List<Id>>>>

So the values of this dictionary would be

{1, ({10,(0001,0012)}, {20,(0010,0014)}, {30, (0002, 0005)})},
{2, ({20,(0001, 0010)}, {30, (0020)} )}
Anthony Mastrean
  • 21,850
  • 21
  • 110
  • 188
Graviton
  • 81,782
  • 146
  • 424
  • 602

3 Answers3

28

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
  • 1,421,763
  • 867
  • 9,128
  • 9,194
16

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
            }
    };
Amy B
  • 108,202
  • 21
  • 135
  • 185
thinkbeforecoding
  • 6,668
  • 1
  • 29
  • 31
14

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()
        )
   )
Amy B
  • 108,202
  • 21
  • 135
  • 185