Créer un dictionnaire sur une liste avec regroupement

106

J'ai l'objet suivant dans une liste:

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

Maintenant, je veux créer le dictionnaire suivant à partir de celui-ci:

Dictionary<int, List<DemoClass>>

Je veux regrouper le List<DemoClass>par propriété GroupKey, mais je ne comprends pas comment cela est fait et un peu d'aide.

Après avoir réfléchi un peu, j'ai obtenu le comportement nécessaire avec:

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());

mais, y a-t-il un moyen d'en faire une seule déclaration?

Andreas Niedermair
la source

Réponses:

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

Celui-ci fonctionnera !!!

Prashant Cholachagudda
la source
198

Juste pour concrétiser la suggestion de mquander :

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

Vous le raccourciriez si vous utilisiez également des noms de variables plus courts, bien sûr :)

Cependant, puis-je suggérer qu'une recherche pourrait être plus appropriée? Une recherche est essentiellement un dictionnaire d'une clé à une IEnumerable<T>- à moins que vous n'ayez vraiment besoin des valeurs sous forme de liste, cela rend le code encore plus court (et plus efficace) avec l' appel ToLookup :

var groupedDemoClasses = mySpecialVariableWhichIsAListOfDemoClass
                             .ToLookup(x => x.GroupKey);
Jon Skeet
la source
1
Je pensais qu'une recherche ne fonctionnait pas très bien, par rapport à un dictionnaire construit, dans un environnement à long terme, car elle génère un résultat frais pour chaque demande ... veuillez me corriger, si je me trompe!
Andreas Niedermair
Non, cela crée toute la recherche. En général, ToXXX n'utilise pas l'exécution différée.
Jon Skeet le
1
(Vous pensez peut-être à un regroupement, qui est en effet différé.)
Jon Skeet
1
Si je n'étais pas si méchant, je voterais pour toi. Je suis venu pour le Linq, je suis resté pour la structure de données dont je n'ai jamais entendu parler!
Chris McCall
2
@sasikt: Le modèle est que vous pouvez rechercher n'importe quoi , et vous obtenez juste une collection vide si la clé n'existe pas. C'est souvent plus utile que l'approche TryGetValue, IMO.
Jon Skeet
6

Vous en avez déjà fait un one-liner. Mettez simplement le ToDictionaryà la fin de votre première ligne. Si vous souhaitez qu'elle soit plus courte, utilisez la syntaxe de composition fonctionnelle au lieu de la syntaxe de requête.

mqp
la source
3

Je vais légèrement hors sujet ici, mais je suis arrivé à ce fil parce que je cherchais un moyen de créer un dictionnaire d'un dictionnaire en Linq, et la conversation ici m'a conduit à la réponse ...

Vous pouvez utiliser linq pour créer des dictionnaires à plusieurs niveaux, ce qui est utile pour les scénarios où vous avez plus d'une clé ou dimension que vous souhaitez rechercher. L'astuce consiste à créer un regroupement puis à le convertir en dictionnaire, comme suit:

  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)

La requête résultante peut être utilisée comme suit:

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

J'espère que cela sera utile à quiconque a besoin de ce type de requête.

marque
la source