Crie um dicionário em uma lista com agrupamento

106

Tenho o seguinte objeto em uma lista:

public class DemoClass
{
    public int GroupKey { get; set; }
    public string DemoString { get; set; }
    public object SomeOtherProperty { get; set; }
}

Agora, quero criar o seguinte dicionário a partir dele:

Dictionary<int, List<DemoClass>>

Quero agrupar o List<DemoClass>pela propriedade GroupKey, mas não entendo como se faz e alguma ajuda.

Depois de pensar um pouco, consegui o comportamento necessário com:

var groupedDemoClasses = from demoClass in mySepcialVariableWhichIsAListOfDemoClass
                            group demoClass by demoClass.GroupKey
                            into groupedDemoClass
                            select groupedDemoClass;
var neededDictionary = groupedDemoClass.ToDictionary(gdc => gdc.Key, gdc => gdc.ToList());

mas há uma maneira de transformar isso em uma única afirmação?

Andreas Niedermair
fonte

Respostas:

88
var groupedDemoClasses = (from demoClass in mySepcialVariableWhichIsAListOfDemoClass
                          group demoClass by demoClass.GroupKey
                          into groupedDemoClass
                          select groupedDemoClass).ToDictionary(gdc => gdc.Key, gdc => gdc.ToList());

Este vai funcionar !!!

Prashant Cholachagudda
fonte
198

Apenas para tornar concreta a sugestão de mquander :

var groupedDemoClasses = mySpecialVariableWhichIsAListOfDemoClass
                             .GroupBy(x => x.GroupKey)
                             .ToDictionary(gdc => gdc.Key, gdc => gdc.ToList());

Você o tornaria mais curto se usasse nomes de variáveis ​​mais curtos também, é claro :)

No entanto, posso sugerir que uma pesquisa pode ser mais apropriada? Um Lookup é basicamente um dicionário de uma chave para uma IEnumerable<T>- a menos que você realmente precise dos valores como uma lista, torna o código ainda mais curto (e mais eficiente) com a chamada ToLookup :

var groupedDemoClasses = mySpecialVariableWhichIsAListOfDemoClass
                             .ToLookup(x => x.GroupKey);
Jon Skeet
fonte
1
Achei que uma consulta não funcionava muito bem, comparada a um dicionário construído, em um ambiente de longo prazo, pois cria o resultado fresco a cada solicitação ... corrija-me, se eu estiver errado!
Andreas Niedermair
Não, ele cria toda a pesquisa. Em geral, ToXXX não usa execução adiada.
Jon Skeet
1
(Você pode estar pensando em um agrupamento, que de fato foi adiado.)
Jon Skeet
1
Se eu não fosse tão rancoroso, votaria em você. Vim pelo Linq, fiquei pela estrutura de dados da qual nunca ouvi falar!
Chris McCall
2
@sasikt: O modelo é que você pode pesquisar qualquer coisa e apenas obter uma coleção vazia se a chave não existir. Isso geralmente é mais útil do que a abordagem TryGetValue, IMO.
Jon Skeet
6

Você já fez um one-liner. Basta colocar ToDictionaryno final da primeira linha. Se você quiser que seja mais curto, use a sintaxe de composição funcional em vez da sintaxe de consulta.

mqp
fonte
3

Estou saindo um pouco do assunto aqui, mas cheguei a este tópico porque estava procurando uma maneira de criar um dicionário de um dicionário no Linq, e a conversa aqui me levou à resposta ...

Você pode usar o linq para criar dicionários de vários níveis, o que é útil para cenários onde você tem mais de uma chave ou dimensão pela qual deseja pesquisar. O truque é criar um agrupamento e depois convertê-lo em um dicionário, da seguinte maneira:

  Dim qry = (From acs In ActualSales _
             Group By acs.ProductID Into Group _
             Select ProductID, Months = Group.ToDictionary(Function(c) c.Period) _
            ).ToDictionary(Function(c) c.ProductID)

A consulta resultante pode ser usada da seguinte maneira:

 If qry.ContainsKey(_ProductID) Then
      With qry(_ProductID)
          If .Months.ContainsKey(_Period) Then
             ...
          End If
      End With
 End If

Espero que isso seja útil para qualquer pessoa que precise desse tipo de consulta.

Marca
fonte